finalize schema/data setup

Signed-off-by: Folling <mail@folling.io>
This commit is contained in:
folling 2025-01-01 13:49:05 +01:00 committed by Folling
parent 4d7bf09c4e
commit 954b8a11a3
Signed by: folling
SSH key fingerprint: SHA256:S9qEx5WCFFLK49tE/LKnKuJYM5sw+++Dn6qJbbyxnCY
89 changed files with 2324 additions and 6271 deletions

View file

@ -109,7 +109,9 @@ IncludeCategories:
Priority: 11 Priority: 11
- Regex: '^<sqlitecpp/.*>$' - Regex: '^<sqlitecpp/.*>$'
Priority: 12 Priority: 12
- Regex: '^<ikarus/.*>$' - Regex: '^<ikarus/.*\.h>$'
Priority: 13
- Regex: '^<ikarus/.*\.hpp>$'
Priority: 13 Priority: 13
IndentAccessModifiers: false IndentAccessModifiers: false
@ -189,4 +191,4 @@ SpacesInSquareBrackets: false
Standard: c++20 Standard: c++20
TabWidth: 4 TabWidth: 4
UseTab: Never UseTab: Always

3
.gitmodules vendored
View file

@ -10,3 +10,6 @@
[submodule "vendor/cppbase"] [submodule "vendor/cppbase"]
path = vendor/cppbase path = vendor/cppbase
url = ssh://git@git.rewritesarebliss.com:16658/Folling/cppbase.git url = ssh://git@git.rewritesarebliss.com:16658/Folling/cppbase.git
[submodule "vendor/json"]
path = vendor/json
url = git@github.com:nlohmann/json.git

View file

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.18) cmake_minimum_required(VERSION 3.31)
project(ikarus) project(ikarus)
option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF) option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF)
@ -9,7 +9,7 @@ add_subdirectory(vendor)
add_subdirectory(include) add_subdirectory(include)
add_subdirectory(src) add_subdirectory(src)
find_package(Boost COMPONENTS system filesystem REQUIRED) find_package(Boost CONFIG COMPONENTS system filesystem REQUIRED)
add_library( add_library(
ikarus SHARED ikarus SHARED
@ -17,40 +17,31 @@ add_library(
${SOURCE_FILES} ${SOURCE_FILES}
) )
add_dependencies(
ikarus
flatbuffer_headers
)
set_target_properties( set_target_properties(
ikarus PROPERTIES ikarus PROPERTIES
CXX_STANDARD 23 CXX_STANDARD 23
CXX_STANDARD_REQUIRED ON CXX_STANDARD_REQUIRED ON
LINKER_LANGUAGE CXX
POSITION_INDEPENDENT_CODE TRUE POSITION_INDEPENDENT_CODE TRUE
) )
target_include_directories( target_include_directories(
ikarus PUBLIC ikarus
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/include ${CMAKE_CURRENT_LIST_DIR}/include
) PRIVATE
target_include_directories(
ikarus PRIVATE
${CMAKE_CURRENT_LIST_DIR}/src ${CMAKE_CURRENT_LIST_DIR}/src
${Boost_INCLUDE_DIR}
) )
target_link_libraries( target_link_libraries(
ikarus PRIVATE ikarus PRIVATE
cppbase cppbase
sqlitecpp sqlitecpp
nlohmann_json::nlohmann_json
${Boost_LIBRARIES} ${Boost_LIBRARIES}
) )
target_include_directories(
ikarus PRIVATE
${Boost_INCLUDE_DIR}
)
if (LIBIKARUS_ENABLE_LINTS) if (LIBIKARUS_ENABLE_LINTS)
find_program(IWYU_PATH NAMES include-what-you-use iwyu REQUIRED) find_program(IWYU_PATH NAMES include-what-you-use iwyu REQUIRED)
find_program(CLANG_TIDY_PATH NAMES clang-tidy REQUIRED) find_program(CLANG_TIDY_PATH NAMES clang-tidy REQUIRED)
@ -66,7 +57,7 @@ if (LIBIKARUS_BUILD_DOCS)
find_program(DOXYGEN_PATH NAMES doxygen REQUIRED) find_program(DOXYGEN_PATH NAMES doxygen REQUIRED)
add_custom_target( add_custom_target(
libikarus_docs libikarus_docs
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/docs WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/docs/doxygen
COMMAND ${DOXYGEN_PATH} DoxyFile COMMAND ${DOXYGEN_PATH} DoxyFile
COMMENT "Generating documentation with Doxygen" COMMENT "Generating documentation with Doxygen"
VERBATIM VERBATIM

35
CMakePresets.json Normal file
View file

@ -0,0 +1,35 @@
{
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 23,
"patch": 0
},
"configurePresets": [
{
"name": "default-mac",
"displayName": "Default Config for MacOS",
"description": "Default MacOS build using Ninja generator",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/default",
"cacheVariables": {
"CMAKE_COLOR_DIAGNOSTICS": "ON",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
},
"toolchainFile": "tools/cmake/toolchains/mac.cmake"
},
{
"name": "test-mac",
"displayName": "Test Config MacOS",
"description": "Test MacOS build using Ninja generator",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/test",
"cacheVariables": {
"CMAKE_COLOR_DIAGNOSTICS": "ON",
"LIBIKARUS_ENABLE_TESTS": "ON",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
},
"toolchainFile": "tools/cmake/toolchains/mac.cmake"
}
]
}

View file

@ -1,4 +1,4 @@
Copyright 2019-2023 Folling (folling@ikarus.world) Copyright 2019-2024 Folling (mail@folling.io)
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met: following conditions are met:

5
docs/Flags.md Normal file
View file

@ -0,0 +1,5 @@
All mutating functions take a flag argument that can be used to control the behavior of the function. The flags are
defined in a corresponding enum. The flags are passed as a bitset, so multiple flags can be passed at once by using the
bitwise OR operator `|`.
The reason for this feature is ABI stability.

7
docs/Ownership.md Normal file
View file

@ -0,0 +1,7 @@
### Getters
Returned data remains owned by libikarus, the exception are Entity(Property)Values.
### Setters
libikarus assumes ownership of any data passed into it, the exception are Entity(Property)Values.

2
docs/internal/Caching.md Normal file
View file

@ -0,0 +1,2 @@
libikarus doesn't perform any caching as the filesystem & SQLite already cache quite well.
Until we can see that there's a performance overhead no caching will be implemented.

View file

@ -5,5 +5,3 @@ file(
) )
set(INCLUDE_FILES ${FILES} PARENT_SCOPE) set(INCLUDE_FILES ${FILES} PARENT_SCOPE)
add_subdirectory(ikarus)

View file

@ -1 +0,0 @@
add_subdirectory(models)

View file

@ -1,19 +1,17 @@
#pragma once #pragma once
/// \file errors.h /// \file errors.h
/// \author Folling <folling@ikarus.world> /// \author Folling <mail@folling.io>
#include <ikarus/macros.h> #include <ikarus/macros.h>
/// \addtogroup errors Errors /// \addtogroup errors Errors
/// \brief Error handling within libikarus /// \brief Error handling within libikarus
/// \details Functions in Ikarus may fail. To report the type of failure all functions have an out parameter for the error. /// \details Functions in Ikarus may fail. To report the type of failure all functions have an out parameter for the error.
/// Upon erring the function will store relevant information about the error in the out parameter. /// Upon erring the function will store relevant information about the error in
/// If the out parameter is null nothing will be stored. This is not recommended as it essentially ignores errors. /// the out parameter. If the out parameter is null nothing will be stored. This
/// For the sake of simplicity we have avoided mechanisms that "force" clients to handle errors. /// is not recommended as it essentially ignores errors. For the sake of
/// Note that Ikarus does not check for null pointers. Passing null pointers to functions that do not explicitly state that they accept null /// simplicity we have avoided mechanisms that "force" clients to handle errors.
/// 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 IKARUS_BEGIN_HEADER
@ -22,73 +20,77 @@ IKARUS_BEGIN_HEADER
/// \remark Note that this doesn't show responsibility. An error with source "SubSystem" could still be the /// \remark Note that this doesn't show responsibility. An error with source "SubSystem" could still be the
/// fault of libikarus, it just indicates where the error occurred. /// fault of libikarus, it just indicates where the error occurred.
enum IkarusErrorInfo { enum IkarusErrorInfo {
/// \brief No error occurred. /// \brief No error occurred.
IkarusErrorInfo_None = 0x0, IkarusErrorInfo_None = 0x0,
/// \brief The client misused the API. /// \brief The client misused the API.
/// Example: Accessing a resource that does not exist. /// Example: Accessing a resource that does not exist.
IkarusErrorInfo_Client_Misuse = 0x01000001, IkarusErrorInfo_Client_Misuse = 0x01000001,
/// \brief The client provided a null value for a parameter that must not be null. /// \brief The client provided a null value for a parameter that must not be null.
/// Example: Passing null for `ikarus_project_get_name` /// Example: Passing null for `ikarus_project_get_name`
IkarusErrorInfo_Client_InvalidNull = 0x01000002, IkarusErrorInfo_Client_InvalidNull = 0x01000002,
/// \brief The client provided an index that was out of bounds for some array. /// \brief The client provided a non-existent resource.
/// Example: Passing the index 3 for an `IkarusToggleValue` with size 3. /// Example: Passing an entity to a function after it has been deleted.
IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000003, IkarusErrorInfo_Client_NonExistent = 0x01000003,
/// \brief The client provided a numeric value that was out of bounds /// \brief The client provided an index that was out of bounds for some array.
/// Example: Passing the value 2^32 to an i32 (might be passed as a string). /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3.
IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000004, IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000004,
/// \brief The client provided invalid input that doesn't fit in any of the other categories. /// \brief The client provided a numeric value that was out of bounds
/// Example: Passing an empty/blank string for a string that must be non-empty/-blank. /// Example: Passing the value 2^32 to an i32 (might be passed as a string).
IkarusErrorInfo_Client_InvalidInput = 0x01000005, IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000005,
/// \brief The client provided valid data in an invalid format. /// \brief The client provided invalid input that doesn't fit in any of the other categories.
/// Example: Passing a malformed JSON string. /// Example: Passing an empty/blank string for a string that must be
IkarusErrorInfo_Client_InvalidFormat = 0x01000006, /// non-empty/-blank.
/// \brief The client violated a constraint. IkarusErrorInfo_Client_InvalidInput = 0x01000006,
/// \details This error is most likely caused by endusers. /// \brief The client provided valid data in an invalid format.
/// Example: A user tries to set the age of a character to an value outside of their specified range. /// Example: Passing a malformed JSON string.
IkarusErrorInfo_Client_ConstraintViolated = 0x10000007, IkarusErrorInfo_Client_InvalidFormat = 0x01000007,
/// \brief The client violated a constraint.
/// \details This error is most likely caused by clients.
/// Example: A user tries to set the age of a character to a value outside
/// its specified range.
IkarusErrorInfo_Client_ConstraintViolated = 0x10000008,
// 0x02 reserved for dependency errors // 0x02 reserved for dependency errors
/// \brief A file or directory already exists. /// \brief A file was not found.
IkarusErrorInfo_Filesystem_AccessIssue = 0x03000001, IkarusErrorInfo_Filesystem_NotFound = 0x03000001,
/// \brief A file was not found. /// \brief A file or directory already exists.
IkarusErrorInfo_Filesystem_NotFound = 0x03000002, IkarusErrorInfo_Filesystem_AlreadyExists = 0x03000002,
/// \brief A file or directory already exists. /// \brief Missing permissions to access a file or directory.
IkarusErrorInfo_Filesystem_AlreadyExists = 0x03000003, IkarusErrorInfo_Filesystem_MissingPermissions = 0x03000003,
/// \brief Missing permissions to access a file or directory. /// \brief Insufficient space to perform an operation.
IkarusErrorInfo_Filesystem_MissingPermissions = 0x03000004, IkarusErrorInfo_Filesystem_InsufficientSpace = 0x03000004,
/// \brief Insufficient space to perform an operation. /// \brief A path is invalid.
IkarusErrorInfo_Filesystem_InsufficientSpace = 0x03000005, IkarusErrorInfo_Filesystem_InvalidPath = 0x03000005,
/// \brief A path is invalid.
IkarusErrorInfo_Filesystem_InvalidPath = 0x03000006,
/// \brief A database connection failed. /// \brief A database connection failed.
IkarusErrorInfo_Database_ConnectionFailed = 0x04000001, IkarusErrorInfo_Database_ConnectionFailed = 0x04000001,
/// \brief A database query failed. /// \brief A database query failed.
IkarusErrorInfo_Database_QueryFailed = 0x04000002, IkarusErrorInfo_Database_QueryFailed = 0x04000002,
/// \brief A database migration failed. /// \brief A database migration failed.
IkarusErrorInfo_Database_MigrationFailed = 0x04000003, IkarusErrorInfo_Database_MigrationFailed = 0x04000003,
/// \brief A database is in an invalid state. This indicates a corrupt project. /// \brief A database is in an invalid state. This indicates a corrupt project.
/// Example: An entity is linked to a non-existant blueprint. /// Example: An entity is linked to a non-existent blueprint.
IkarusErrorInfo_Database_InvalidState = 0x04000004, IkarusErrorInfo_Database_InvalidState = 0x04000004,
/// \brief A system call failed. /// \brief A system call failed.
IkarusErrorInfo_OS_SystemCallFailed = 0x05000001, IkarusErrorInfo_OS_SystemCallFailed = 0x05000001,
/// \brief A system call returned an invalid value. /// \brief A system call returned an invalid value.
IkarusErrorInfo_OS_InvalidReturnValue = 0x05000002, IkarusErrorInfo_OS_InvalidReturnValue = 0x05000002,
/// \brief An OOM error occurred. /// \brief An OOM error occurred.
IkarusErrorInfo_OS_InsufficientMemory = 0x05000003, IkarusErrorInfo_OS_InsufficientMemory = 0x05000003,
/// \brief A datapoint within ikarus is invalid for the current state of the system. /// \brief A datapoint within ikarus is invalid for the current state of the system.
/// \details This differs from IkarusErrorInfo_Database_InvalidState in that the latter implies the database itself holds invalid state, /// \details This differs from IkarusErrorInfo_Database_InvalidState in that the latter implies the database itself holds invalid state,
/// whereas the former may imply that the state is ephemeral, e.g. data within a function. /// whereas the former may imply that the state is ephemeral, e.g. data
/// Example: The name of an object is found to be invalid UTF8. /// within a function.
IkarusErrorInfo_LibIkarus_InvalidState = 0x06000001, /// Example: The name of an object is found to be invalid UTF8.
/// \brief libikarus is unable to perform a certain operation that should succeed. IkarusErrorInfo_LibIkarus_InvalidState = 0x06000001,
IkarusErrorInfo_LibIkarus_CannotPerformOperation = 0x06000002, /// \brief libikarus is unable to perform a certain operation that should succeed.
/// \brief libikarus is unable to perform a certain operation within a given timeframe. IkarusErrorInfo_LibIkarus_CannotPerformOperation = 0x06000002,
/// Example: A query takes longer than the timeout. /// \brief libikarus is unable to perform a certain operation within a given timeframe.
IkarusErrorInfo_LibIkarus_Timeout = 0x06000003, /// Example: A query takes longer than the timeout.
IkarusErrorInfo_LibIkarus_Timeout = 0x06000003,
}; };
/// \brief The maximum length of an error message. /// \brief The maximum length of an error message.
@ -96,17 +98,17 @@ enum IkarusErrorInfo {
/// \brief The data stored for an error /// \brief The data stored for an error
struct IkarusErrorData { struct IkarusErrorData {
/// \brief The error type /// \brief The error type
IkarusErrorInfo info; enum IkarusErrorInfo info;
char message[IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT]; char message[IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT];
}; };
/// \brief Gets the name of an error info. /// \brief Gets the name of an error info.
/// \param info The error info to get the name of. /// \param info The error info to get the name of.
/// \return The name of the error info. /// \return The name of the error info.
/// \remark The returned pointer is valid for the lifetime of the program and must not be freed. /// \remark The returned pointer is valid for the lifetime of the program and must not be freed.
IKA_API char const * ikarus_get_error_info_name(IkarusErrorInfo info); IKA_API char const * ikarus_error_info_get_name(enum IkarusErrorInfo info);
/// \brief Checks if an error data is a success. /// \brief Checks if an error data is a success.
/// \param data The error data to check. /// \param data The error data to check.

View file

@ -1,41 +0,0 @@
file(
GLOB_RECURSE
FLATBUFFER_SOURCES
"*.fbs"
)
foreach (FLATBUFFER_SOURCE IN LISTS ${FLATBUFFER_SOURCES})
cmake_path(
GET
${FLATBUFFER_SOURCE}
FILENAME
FLATBUFFER_SOURCE_NAME
)
string(
CONCAT
FLATBUFFER_GENERATED_SOURCE_NAME
${FLATBUFFER_SOURCE_NAME}
"_generated"
)
cmake_path(
REPLACE_EXTENSION
${FLATBUFFER_GENERATED_SOURCE_NAME}
".h"
OUTPUT_VARIABLE
FLATBUFFER_GENERATED_HEADER
)
list(APPEND FLATBUFFER_GENERATED_HEADERS ${FLATBUFFER_GENERATED_HEADER})
endforeach ()
add_custom_target(
flatbuffer_headers
COMMENT "Generating flatbuffer headers"
DEPENDS ${FLATBUFFER_SOURCES}
BYPRODUCTS ${FLATBUFFER_GENERATED_HEADERS}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND flatc --cpp --cpp-std "c++17" ${FLATBUFFER_SOURCES}
VERBATIM
)

View file

@ -1,12 +0,0 @@
include "property.fbs";
namespace Ikarus;
table Blueprint {
id: int64;
name: string;
description: string;
tags: [string];
}
root_type Blueprint;

View file

@ -1,153 +0,0 @@
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_BLUEPRINT_IKARUS_H_
#define FLATBUFFERS_GENERATED_BLUEPRINT_IKARUS_H_
#include "flatbuffers/flatbuffers.h"
// Ensure the included flatbuffers.h is the same version as when this file was
// generated, otherwise it may not be compatible.
static_assert(FLATBUFFERS_VERSION_MAJOR == 24 &&
FLATBUFFERS_VERSION_MINOR == 3 &&
FLATBUFFERS_VERSION_REVISION == 25,
"Non-compatible flatbuffers version included");
#include "property_generated.h"
namespace Ikarus {
struct Blueprint;
struct BlueprintBuilder;
struct Blueprint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef BlueprintBuilder Builder;
struct Traits;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_ID = 4,
VT_NAME = 6,
VT_DESCRIPTION = 8,
VT_TAGS = 10
};
int64_t id() const {
return GetField<int64_t>(VT_ID, 0);
}
const ::flatbuffers::String *name() const {
return GetPointer<const ::flatbuffers::String *>(VT_NAME);
}
const ::flatbuffers::String *description() const {
return GetPointer<const ::flatbuffers::String *>(VT_DESCRIPTION);
}
const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const {
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *>(VT_TAGS);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int64_t>(verifier, VT_ID, 8) &&
VerifyOffset(verifier, VT_NAME) &&
verifier.VerifyString(name()) &&
VerifyOffset(verifier, VT_DESCRIPTION) &&
verifier.VerifyString(description()) &&
VerifyOffset(verifier, VT_TAGS) &&
verifier.VerifyVector(tags()) &&
verifier.VerifyVectorOfStrings(tags()) &&
verifier.EndTable();
}
};
struct BlueprintBuilder {
typedef Blueprint Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_id(int64_t id) {
fbb_.AddElement<int64_t>(Blueprint::VT_ID, id, 0);
}
void add_name(::flatbuffers::Offset<::flatbuffers::String> name) {
fbb_.AddOffset(Blueprint::VT_NAME, name);
}
void add_description(::flatbuffers::Offset<::flatbuffers::String> description) {
fbb_.AddOffset(Blueprint::VT_DESCRIPTION, description);
}
void add_tags(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> tags) {
fbb_.AddOffset(Blueprint::VT_TAGS, tags);
}
explicit BlueprintBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<Blueprint> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<Blueprint>(end);
return o;
}
};
inline ::flatbuffers::Offset<Blueprint> CreateBlueprint(
::flatbuffers::FlatBufferBuilder &_fbb,
int64_t id = 0,
::flatbuffers::Offset<::flatbuffers::String> name = 0,
::flatbuffers::Offset<::flatbuffers::String> description = 0,
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> tags = 0) {
BlueprintBuilder builder_(_fbb);
builder_.add_id(id);
builder_.add_tags(tags);
builder_.add_description(description);
builder_.add_name(name);
return builder_.Finish();
}
struct Blueprint::Traits {
using type = Blueprint;
static auto constexpr Create = CreateBlueprint;
};
inline ::flatbuffers::Offset<Blueprint> CreateBlueprintDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
int64_t id = 0,
const char *name = nullptr,
const char *description = nullptr,
const std::vector<::flatbuffers::Offset<::flatbuffers::String>> *tags = nullptr) {
auto name__ = name ? _fbb.CreateString(name) : 0;
auto description__ = description ? _fbb.CreateString(description) : 0;
auto tags__ = tags ? _fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::String>>(*tags) : 0;
return Ikarus::CreateBlueprint(
_fbb,
id,
name__,
description__,
tags__);
}
inline const Ikarus::Blueprint *GetBlueprint(const void *buf) {
return ::flatbuffers::GetRoot<Ikarus::Blueprint>(buf);
}
inline const Ikarus::Blueprint *GetSizePrefixedBlueprint(const void *buf) {
return ::flatbuffers::GetSizePrefixedRoot<Ikarus::Blueprint>(buf);
}
inline bool VerifyBlueprintBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<Ikarus::Blueprint>(nullptr);
}
inline bool VerifySizePrefixedBlueprintBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<Ikarus::Blueprint>(nullptr);
}
inline void FinishBlueprintBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<Ikarus::Blueprint> root) {
fbb.Finish(root);
}
inline void FinishSizePrefixedBlueprintBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<Ikarus::Blueprint> root) {
fbb.FinishSizePrefixed(root);
}
} // namespace Ikarus
#endif // FLATBUFFERS_GENERATED_BLUEPRINT_IKARUS_H_

View file

@ -1,26 +0,0 @@
include "value.fbs";
include "property.fbs";
include "blueprint.fbs";
namespace Ikarus;
table NamedValue {
name: string (key);
value: Ikarus.Value.Value;
}
table PropertyValue {
property_id: int64 (key);
data: Ikarus.Value.Data;
}
table Entity {
id: int64;
name: string;
description: string;
tags: [string];
values: [NamedValue];
property_values: [PropertyValue];
}
root_type Entity;

View file

@ -1,398 +0,0 @@
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_ENTITY_IKARUS_H_
#define FLATBUFFERS_GENERATED_ENTITY_IKARUS_H_
#include "flatbuffers/flatbuffers.h"
// Ensure the included flatbuffers.h is the same version as when this file was
// generated, otherwise it may not be compatible.
static_assert(FLATBUFFERS_VERSION_MAJOR == 24 &&
FLATBUFFERS_VERSION_MINOR == 3 &&
FLATBUFFERS_VERSION_REVISION == 25,
"Non-compatible flatbuffers version included");
#include "blueprint_generated.h"
#include "property_generated.h"
#include "value_generated.h"
namespace Ikarus {
struct NamedValue;
struct NamedValueBuilder;
struct PropertyValue;
struct PropertyValueBuilder;
struct Entity;
struct EntityBuilder;
struct NamedValue FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef NamedValueBuilder Builder;
struct Traits;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_NAME = 4,
VT_VALUE = 6
};
const ::flatbuffers::String *name() const {
return GetPointer<const ::flatbuffers::String *>(VT_NAME);
}
bool KeyCompareLessThan(const NamedValue * const o) const {
return *name() < *o->name();
}
int KeyCompareWithValue(const char *_name) const {
return strcmp(name()->c_str(), _name);
}
template<typename StringType>
int KeyCompareWithValue(const StringType& _name) const {
if (name()->c_str() < _name) return -1;
if (_name < name()->c_str()) return 1;
return 0;
}
const Ikarus::Value::Value *value() const {
return GetPointer<const Ikarus::Value::Value *>(VT_VALUE);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffsetRequired(verifier, VT_NAME) &&
verifier.VerifyString(name()) &&
VerifyOffset(verifier, VT_VALUE) &&
verifier.VerifyTable(value()) &&
verifier.EndTable();
}
};
struct NamedValueBuilder {
typedef NamedValue Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_name(::flatbuffers::Offset<::flatbuffers::String> name) {
fbb_.AddOffset(NamedValue::VT_NAME, name);
}
void add_value(::flatbuffers::Offset<Ikarus::Value::Value> value) {
fbb_.AddOffset(NamedValue::VT_VALUE, value);
}
explicit NamedValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<NamedValue> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<NamedValue>(end);
fbb_.Required(o, NamedValue::VT_NAME);
return o;
}
};
inline ::flatbuffers::Offset<NamedValue> CreateNamedValue(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::String> name = 0,
::flatbuffers::Offset<Ikarus::Value::Value> value = 0) {
NamedValueBuilder builder_(_fbb);
builder_.add_value(value);
builder_.add_name(name);
return builder_.Finish();
}
struct NamedValue::Traits {
using type = NamedValue;
static auto constexpr Create = CreateNamedValue;
};
inline ::flatbuffers::Offset<NamedValue> CreateNamedValueDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
const char *name = nullptr,
::flatbuffers::Offset<Ikarus::Value::Value> value = 0) {
auto name__ = name ? _fbb.CreateString(name) : 0;
return Ikarus::CreateNamedValue(
_fbb,
name__,
value);
}
struct PropertyValue FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef PropertyValueBuilder Builder;
struct Traits;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_PROPERTY_ID = 4,
VT_DATA_TYPE = 6,
VT_DATA = 8
};
int64_t property_id() const {
return GetField<int64_t>(VT_PROPERTY_ID, 0);
}
bool KeyCompareLessThan(const PropertyValue * const o) const {
return property_id() < o->property_id();
}
int KeyCompareWithValue(int64_t _property_id) const {
return static_cast<int>(property_id() > _property_id) - static_cast<int>(property_id() < _property_id);
}
Ikarus::Value::Data data_type() const {
return static_cast<Ikarus::Value::Data>(GetField<uint8_t>(VT_DATA_TYPE, 0));
}
const void *data() const {
return GetPointer<const void *>(VT_DATA);
}
template<typename T> const T *data_as() const;
const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const {
return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast<const Ikarus::Value::ToggleDataPoint *>(data()) : nullptr;
}
const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const {
return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast<const Ikarus::Value::NumberDataPoint *>(data()) : nullptr;
}
const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const {
return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast<const Ikarus::Value::TextDataPoint *>(data()) : nullptr;
}
const Ikarus::Value::SimpleData *data_as_SimpleData() const {
return data_type() == Ikarus::Value::Data::SimpleData ? static_cast<const Ikarus::Value::SimpleData *>(data()) : nullptr;
}
const Ikarus::Value::CombinedData *data_as_CombinedData() const {
return data_type() == Ikarus::Value::Data::CombinedData ? static_cast<const Ikarus::Value::CombinedData *>(data()) : nullptr;
}
const Ikarus::Value::ListData *data_as_ListData() const {
return data_type() == Ikarus::Value::Data::ListData ? static_cast<const Ikarus::Value::ListData *>(data()) : nullptr;
}
const Ikarus::Value::ComplexData *data_as_ComplexData() const {
return data_type() == Ikarus::Value::Data::ComplexData ? static_cast<const Ikarus::Value::ComplexData *>(data()) : nullptr;
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int64_t>(verifier, VT_PROPERTY_ID, 8) &&
VerifyField<uint8_t>(verifier, VT_DATA_TYPE, 1) &&
VerifyOffset(verifier, VT_DATA) &&
VerifyData(verifier, data(), data_type()) &&
verifier.EndTable();
}
};
template<> inline const Ikarus::Value::ToggleDataPoint *PropertyValue::data_as<Ikarus::Value::ToggleDataPoint>() const {
return data_as_ToggleDataPoint();
}
template<> inline const Ikarus::Value::NumberDataPoint *PropertyValue::data_as<Ikarus::Value::NumberDataPoint>() const {
return data_as_NumberDataPoint();
}
template<> inline const Ikarus::Value::TextDataPoint *PropertyValue::data_as<Ikarus::Value::TextDataPoint>() const {
return data_as_TextDataPoint();
}
template<> inline const Ikarus::Value::SimpleData *PropertyValue::data_as<Ikarus::Value::SimpleData>() const {
return data_as_SimpleData();
}
template<> inline const Ikarus::Value::CombinedData *PropertyValue::data_as<Ikarus::Value::CombinedData>() const {
return data_as_CombinedData();
}
template<> inline const Ikarus::Value::ListData *PropertyValue::data_as<Ikarus::Value::ListData>() const {
return data_as_ListData();
}
template<> inline const Ikarus::Value::ComplexData *PropertyValue::data_as<Ikarus::Value::ComplexData>() const {
return data_as_ComplexData();
}
struct PropertyValueBuilder {
typedef PropertyValue Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_property_id(int64_t property_id) {
fbb_.AddElement<int64_t>(PropertyValue::VT_PROPERTY_ID, property_id, 0);
}
void add_data_type(Ikarus::Value::Data data_type) {
fbb_.AddElement<uint8_t>(PropertyValue::VT_DATA_TYPE, static_cast<uint8_t>(data_type), 0);
}
void add_data(::flatbuffers::Offset<void> data) {
fbb_.AddOffset(PropertyValue::VT_DATA, data);
}
explicit PropertyValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<PropertyValue> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<PropertyValue>(end);
return o;
}
};
inline ::flatbuffers::Offset<PropertyValue> CreatePropertyValue(
::flatbuffers::FlatBufferBuilder &_fbb,
int64_t property_id = 0,
Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE,
::flatbuffers::Offset<void> data = 0) {
PropertyValueBuilder builder_(_fbb);
builder_.add_property_id(property_id);
builder_.add_data(data);
builder_.add_data_type(data_type);
return builder_.Finish();
}
struct PropertyValue::Traits {
using type = PropertyValue;
static auto constexpr Create = CreatePropertyValue;
};
struct Entity FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef EntityBuilder Builder;
struct Traits;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_ID = 4,
VT_NAME = 6,
VT_DESCRIPTION = 8,
VT_TAGS = 10,
VT_VALUES = 12,
VT_PROPERTY_VALUES = 14
};
int64_t id() const {
return GetField<int64_t>(VT_ID, 0);
}
const ::flatbuffers::String *name() const {
return GetPointer<const ::flatbuffers::String *>(VT_NAME);
}
const ::flatbuffers::String *description() const {
return GetPointer<const ::flatbuffers::String *>(VT_DESCRIPTION);
}
const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const {
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *>(VT_TAGS);
}
const ::flatbuffers::Vector<::flatbuffers::Offset<Ikarus::NamedValue>> *values() const {
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<Ikarus::NamedValue>> *>(VT_VALUES);
}
const ::flatbuffers::Vector<::flatbuffers::Offset<Ikarus::PropertyValue>> *property_values() const {
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<Ikarus::PropertyValue>> *>(VT_PROPERTY_VALUES);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int64_t>(verifier, VT_ID, 8) &&
VerifyOffset(verifier, VT_NAME) &&
verifier.VerifyString(name()) &&
VerifyOffset(verifier, VT_DESCRIPTION) &&
verifier.VerifyString(description()) &&
VerifyOffset(verifier, VT_TAGS) &&
verifier.VerifyVector(tags()) &&
verifier.VerifyVectorOfStrings(tags()) &&
VerifyOffset(verifier, VT_VALUES) &&
verifier.VerifyVector(values()) &&
verifier.VerifyVectorOfTables(values()) &&
VerifyOffset(verifier, VT_PROPERTY_VALUES) &&
verifier.VerifyVector(property_values()) &&
verifier.VerifyVectorOfTables(property_values()) &&
verifier.EndTable();
}
};
struct EntityBuilder {
typedef Entity Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_id(int64_t id) {
fbb_.AddElement<int64_t>(Entity::VT_ID, id, 0);
}
void add_name(::flatbuffers::Offset<::flatbuffers::String> name) {
fbb_.AddOffset(Entity::VT_NAME, name);
}
void add_description(::flatbuffers::Offset<::flatbuffers::String> description) {
fbb_.AddOffset(Entity::VT_DESCRIPTION, description);
}
void add_tags(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> tags) {
fbb_.AddOffset(Entity::VT_TAGS, tags);
}
void add_values(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<Ikarus::NamedValue>>> values) {
fbb_.AddOffset(Entity::VT_VALUES, values);
}
void add_property_values(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<Ikarus::PropertyValue>>> property_values) {
fbb_.AddOffset(Entity::VT_PROPERTY_VALUES, property_values);
}
explicit EntityBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<Entity> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<Entity>(end);
return o;
}
};
inline ::flatbuffers::Offset<Entity> CreateEntity(
::flatbuffers::FlatBufferBuilder &_fbb,
int64_t id = 0,
::flatbuffers::Offset<::flatbuffers::String> name = 0,
::flatbuffers::Offset<::flatbuffers::String> description = 0,
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> tags = 0,
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<Ikarus::NamedValue>>> values = 0,
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<Ikarus::PropertyValue>>> property_values = 0) {
EntityBuilder builder_(_fbb);
builder_.add_id(id);
builder_.add_property_values(property_values);
builder_.add_values(values);
builder_.add_tags(tags);
builder_.add_description(description);
builder_.add_name(name);
return builder_.Finish();
}
struct Entity::Traits {
using type = Entity;
static auto constexpr Create = CreateEntity;
};
inline ::flatbuffers::Offset<Entity> CreateEntityDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
int64_t id = 0,
const char *name = nullptr,
const char *description = nullptr,
const std::vector<::flatbuffers::Offset<::flatbuffers::String>> *tags = nullptr,
std::vector<::flatbuffers::Offset<Ikarus::NamedValue>> *values = nullptr,
std::vector<::flatbuffers::Offset<Ikarus::PropertyValue>> *property_values = nullptr) {
auto name__ = name ? _fbb.CreateString(name) : 0;
auto description__ = description ? _fbb.CreateString(description) : 0;
auto tags__ = tags ? _fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::String>>(*tags) : 0;
auto values__ = values ? _fbb.CreateVectorOfSortedTables<Ikarus::NamedValue>(values) : 0;
auto property_values__ = property_values ? _fbb.CreateVectorOfSortedTables<Ikarus::PropertyValue>(property_values) : 0;
return Ikarus::CreateEntity(
_fbb,
id,
name__,
description__,
tags__,
values__,
property_values__);
}
inline const Ikarus::Entity *GetEntity(const void *buf) {
return ::flatbuffers::GetRoot<Ikarus::Entity>(buf);
}
inline const Ikarus::Entity *GetSizePrefixedEntity(const void *buf) {
return ::flatbuffers::GetSizePrefixedRoot<Ikarus::Entity>(buf);
}
inline bool VerifyEntityBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<Ikarus::Entity>(nullptr);
}
inline bool VerifySizePrefixedEntityBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<Ikarus::Entity>(nullptr);
}
inline void FinishEntityBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<Ikarus::Entity> root) {
fbb.Finish(root);
}
inline void FinishSizePrefixedEntityBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<Ikarus::Entity> root) {
fbb.FinishSizePrefixed(root);
}
} // namespace Ikarus
#endif // FLATBUFFERS_GENERATED_ENTITY_IKARUS_H_

View file

@ -1,14 +0,0 @@
include "value.fbs";
namespace Ikarus;
table Property {
id: int64;
name: string;
description: string;
tags: [string];
value_schema: Ikarus.Value.Schema;
default_value: Ikarus.Value.Data;
}
root_type Property;

View file

@ -1,289 +0,0 @@
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_PROPERTY_IKARUS_H_
#define FLATBUFFERS_GENERATED_PROPERTY_IKARUS_H_
#include "flatbuffers/flatbuffers.h"
// Ensure the included flatbuffers.h is the same version as when this file was
// generated, otherwise it may not be compatible.
static_assert(FLATBUFFERS_VERSION_MAJOR == 24 &&
FLATBUFFERS_VERSION_MINOR == 3 &&
FLATBUFFERS_VERSION_REVISION == 25,
"Non-compatible flatbuffers version included");
#include "value_generated.h"
namespace Ikarus {
struct Property;
struct PropertyBuilder;
struct Property FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef PropertyBuilder Builder;
struct Traits;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_ID = 4,
VT_NAME = 6,
VT_DESCRIPTION = 8,
VT_TAGS = 10,
VT_VALUE_SCHEMA_TYPE = 12,
VT_VALUE_SCHEMA = 14,
VT_DEFAULT_VALUE_TYPE = 16,
VT_DEFAULT_VALUE = 18
};
int64_t id() const {
return GetField<int64_t>(VT_ID, 0);
}
const ::flatbuffers::String *name() const {
return GetPointer<const ::flatbuffers::String *>(VT_NAME);
}
const ::flatbuffers::String *description() const {
return GetPointer<const ::flatbuffers::String *>(VT_DESCRIPTION);
}
const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const {
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *>(VT_TAGS);
}
Ikarus::Value::Schema value_schema_type() const {
return static_cast<Ikarus::Value::Schema>(GetField<uint8_t>(VT_VALUE_SCHEMA_TYPE, 0));
}
const void *value_schema() const {
return GetPointer<const void *>(VT_VALUE_SCHEMA);
}
template<typename T> const T *value_schema_as() const;
const Ikarus::Value::ConstantSchema *value_schema_as_ConstantSchema() const {
return value_schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast<const Ikarus::Value::ConstantSchema *>(value_schema()) : nullptr;
}
const Ikarus::Value::SimpleSchema *value_schema_as_SimpleSchema() const {
return value_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast<const Ikarus::Value::SimpleSchema *>(value_schema()) : nullptr;
}
const Ikarus::Value::CombinedSchema *value_schema_as_CombinedSchema() const {
return value_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast<const Ikarus::Value::CombinedSchema *>(value_schema()) : nullptr;
}
const Ikarus::Value::ListSchema *value_schema_as_ListSchema() const {
return value_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast<const Ikarus::Value::ListSchema *>(value_schema()) : nullptr;
}
const Ikarus::Value::ComplexSchema *value_schema_as_ComplexSchema() const {
return value_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast<const Ikarus::Value::ComplexSchema *>(value_schema()) : nullptr;
}
Ikarus::Value::Data default_value_type() const {
return static_cast<Ikarus::Value::Data>(GetField<uint8_t>(VT_DEFAULT_VALUE_TYPE, 0));
}
const void *default_value() const {
return GetPointer<const void *>(VT_DEFAULT_VALUE);
}
template<typename T> const T *default_value_as() const;
const Ikarus::Value::ToggleDataPoint *default_value_as_ToggleDataPoint() const {
return default_value_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast<const Ikarus::Value::ToggleDataPoint *>(default_value()) : nullptr;
}
const Ikarus::Value::NumberDataPoint *default_value_as_NumberDataPoint() const {
return default_value_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast<const Ikarus::Value::NumberDataPoint *>(default_value()) : nullptr;
}
const Ikarus::Value::TextDataPoint *default_value_as_TextDataPoint() const {
return default_value_type() == Ikarus::Value::Data::TextDataPoint ? static_cast<const Ikarus::Value::TextDataPoint *>(default_value()) : nullptr;
}
const Ikarus::Value::SimpleData *default_value_as_SimpleData() const {
return default_value_type() == Ikarus::Value::Data::SimpleData ? static_cast<const Ikarus::Value::SimpleData *>(default_value()) : nullptr;
}
const Ikarus::Value::CombinedData *default_value_as_CombinedData() const {
return default_value_type() == Ikarus::Value::Data::CombinedData ? static_cast<const Ikarus::Value::CombinedData *>(default_value()) : nullptr;
}
const Ikarus::Value::ListData *default_value_as_ListData() const {
return default_value_type() == Ikarus::Value::Data::ListData ? static_cast<const Ikarus::Value::ListData *>(default_value()) : nullptr;
}
const Ikarus::Value::ComplexData *default_value_as_ComplexData() const {
return default_value_type() == Ikarus::Value::Data::ComplexData ? static_cast<const Ikarus::Value::ComplexData *>(default_value()) : nullptr;
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int64_t>(verifier, VT_ID, 8) &&
VerifyOffset(verifier, VT_NAME) &&
verifier.VerifyString(name()) &&
VerifyOffset(verifier, VT_DESCRIPTION) &&
verifier.VerifyString(description()) &&
VerifyOffset(verifier, VT_TAGS) &&
verifier.VerifyVector(tags()) &&
verifier.VerifyVectorOfStrings(tags()) &&
VerifyField<uint8_t>(verifier, VT_VALUE_SCHEMA_TYPE, 1) &&
VerifyOffset(verifier, VT_VALUE_SCHEMA) &&
VerifySchema(verifier, value_schema(), value_schema_type()) &&
VerifyField<uint8_t>(verifier, VT_DEFAULT_VALUE_TYPE, 1) &&
VerifyOffset(verifier, VT_DEFAULT_VALUE) &&
VerifyData(verifier, default_value(), default_value_type()) &&
verifier.EndTable();
}
};
template<> inline const Ikarus::Value::ConstantSchema *Property::value_schema_as<Ikarus::Value::ConstantSchema>() const {
return value_schema_as_ConstantSchema();
}
template<> inline const Ikarus::Value::SimpleSchema *Property::value_schema_as<Ikarus::Value::SimpleSchema>() const {
return value_schema_as_SimpleSchema();
}
template<> inline const Ikarus::Value::CombinedSchema *Property::value_schema_as<Ikarus::Value::CombinedSchema>() const {
return value_schema_as_CombinedSchema();
}
template<> inline const Ikarus::Value::ListSchema *Property::value_schema_as<Ikarus::Value::ListSchema>() const {
return value_schema_as_ListSchema();
}
template<> inline const Ikarus::Value::ComplexSchema *Property::value_schema_as<Ikarus::Value::ComplexSchema>() const {
return value_schema_as_ComplexSchema();
}
template<> inline const Ikarus::Value::ToggleDataPoint *Property::default_value_as<Ikarus::Value::ToggleDataPoint>() const {
return default_value_as_ToggleDataPoint();
}
template<> inline const Ikarus::Value::NumberDataPoint *Property::default_value_as<Ikarus::Value::NumberDataPoint>() const {
return default_value_as_NumberDataPoint();
}
template<> inline const Ikarus::Value::TextDataPoint *Property::default_value_as<Ikarus::Value::TextDataPoint>() const {
return default_value_as_TextDataPoint();
}
template<> inline const Ikarus::Value::SimpleData *Property::default_value_as<Ikarus::Value::SimpleData>() const {
return default_value_as_SimpleData();
}
template<> inline const Ikarus::Value::CombinedData *Property::default_value_as<Ikarus::Value::CombinedData>() const {
return default_value_as_CombinedData();
}
template<> inline const Ikarus::Value::ListData *Property::default_value_as<Ikarus::Value::ListData>() const {
return default_value_as_ListData();
}
template<> inline const Ikarus::Value::ComplexData *Property::default_value_as<Ikarus::Value::ComplexData>() const {
return default_value_as_ComplexData();
}
struct PropertyBuilder {
typedef Property Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_id(int64_t id) {
fbb_.AddElement<int64_t>(Property::VT_ID, id, 0);
}
void add_name(::flatbuffers::Offset<::flatbuffers::String> name) {
fbb_.AddOffset(Property::VT_NAME, name);
}
void add_description(::flatbuffers::Offset<::flatbuffers::String> description) {
fbb_.AddOffset(Property::VT_DESCRIPTION, description);
}
void add_tags(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> tags) {
fbb_.AddOffset(Property::VT_TAGS, tags);
}
void add_value_schema_type(Ikarus::Value::Schema value_schema_type) {
fbb_.AddElement<uint8_t>(Property::VT_VALUE_SCHEMA_TYPE, static_cast<uint8_t>(value_schema_type), 0);
}
void add_value_schema(::flatbuffers::Offset<void> value_schema) {
fbb_.AddOffset(Property::VT_VALUE_SCHEMA, value_schema);
}
void add_default_value_type(Ikarus::Value::Data default_value_type) {
fbb_.AddElement<uint8_t>(Property::VT_DEFAULT_VALUE_TYPE, static_cast<uint8_t>(default_value_type), 0);
}
void add_default_value(::flatbuffers::Offset<void> default_value) {
fbb_.AddOffset(Property::VT_DEFAULT_VALUE, default_value);
}
explicit PropertyBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<Property> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<Property>(end);
return o;
}
};
inline ::flatbuffers::Offset<Property> CreateProperty(
::flatbuffers::FlatBufferBuilder &_fbb,
int64_t id = 0,
::flatbuffers::Offset<::flatbuffers::String> name = 0,
::flatbuffers::Offset<::flatbuffers::String> description = 0,
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> tags = 0,
Ikarus::Value::Schema value_schema_type = Ikarus::Value::Schema::NONE,
::flatbuffers::Offset<void> value_schema = 0,
Ikarus::Value::Data default_value_type = Ikarus::Value::Data::NONE,
::flatbuffers::Offset<void> default_value = 0) {
PropertyBuilder builder_(_fbb);
builder_.add_id(id);
builder_.add_default_value(default_value);
builder_.add_value_schema(value_schema);
builder_.add_tags(tags);
builder_.add_description(description);
builder_.add_name(name);
builder_.add_default_value_type(default_value_type);
builder_.add_value_schema_type(value_schema_type);
return builder_.Finish();
}
struct Property::Traits {
using type = Property;
static auto constexpr Create = CreateProperty;
};
inline ::flatbuffers::Offset<Property> CreatePropertyDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
int64_t id = 0,
const char *name = nullptr,
const char *description = nullptr,
const std::vector<::flatbuffers::Offset<::flatbuffers::String>> *tags = nullptr,
Ikarus::Value::Schema value_schema_type = Ikarus::Value::Schema::NONE,
::flatbuffers::Offset<void> value_schema = 0,
Ikarus::Value::Data default_value_type = Ikarus::Value::Data::NONE,
::flatbuffers::Offset<void> default_value = 0) {
auto name__ = name ? _fbb.CreateString(name) : 0;
auto description__ = description ? _fbb.CreateString(description) : 0;
auto tags__ = tags ? _fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::String>>(*tags) : 0;
return Ikarus::CreateProperty(
_fbb,
id,
name__,
description__,
tags__,
value_schema_type,
value_schema,
default_value_type,
default_value);
}
inline const Ikarus::Property *GetProperty(const void *buf) {
return ::flatbuffers::GetRoot<Ikarus::Property>(buf);
}
inline const Ikarus::Property *GetSizePrefixedProperty(const void *buf) {
return ::flatbuffers::GetSizePrefixedRoot<Ikarus::Property>(buf);
}
inline bool VerifyPropertyBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<Ikarus::Property>(nullptr);
}
inline bool VerifySizePrefixedPropertyBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<Ikarus::Property>(nullptr);
}
inline void FinishPropertyBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<Ikarus::Property> root) {
fbb.Finish(root);
}
inline void FinishSizePrefixedPropertyBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<Ikarus::Property> root) {
fbb.FinishSizePrefixed(root);
}
} // namespace Ikarus
#endif // FLATBUFFERS_GENERATED_PROPERTY_IKARUS_H_

View file

@ -1,85 +0,0 @@
namespace Ikarus.Value;
table ToggleDataPoint {
data: [bool];
}
table NumberDataPoint {
data: [double];
}
table TextDataPoint {
data: [string];
}
union Data {
ToggleDataPoint,
NumberDataPoint,
TextDataPoint,
SimpleData,
CombinedData,
ListData,
ComplexData
}
union Schema {
ConstantSchema,
SimpleSchema,
CombinedSchema,
ListSchema,
ComplexSchema
}
table ConstantSchema {
sub_schema: Schema;
data: Data;
}
table SimpleSchema {
sub_schema: Schema;
}
table SimpleData {
data: Data;
}
table CombinedSchema {
schemas: [Schema];
}
table CombinedData {
data: [Data];
}
table ListSchema {
schema: Schema;
}
table ListData {
data: [Data];
}
table NamedSchema {
name: string;
schema: Schema;
}
table ComplexSchema {
schemas: [NamedSchema];
}
table NamedData {
name: string;
data: Data;
}
table ComplexData {
data: [NamedData];
}
table Value {
schema: Schema;
data: Data;
}
root_type Value;

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
/// \file blueprint.h /// \file blueprint.h
/// \author Folling <folling@ikarus.world> /// \author Folling <mail@folling.io>
#include <ikarus/errors.h> #include <ikarus/errors.h>
#include <ikarus/macros.h> #include <ikarus/macros.h>
@ -13,57 +13,113 @@
IKARUS_BEGIN_HEADER IKARUS_BEGIN_HEADER
/// \brief Blueprints are templates for managing common properties of entities. /// \brief Templates for sharing common properties between entities.
/// \details A blueprint is a collection of properties which can be linked to entities. /// \details A blueprint allows sharing properties between entities.
/// Each entity the blueprint is linked to will have values for the blueprints /// Each entity the blueprint is linked to will have a corresponding value for
/// properties. Changes in blueprints will be reflected in all linked entities. /// each of the blueprint's properties. Changes in blueprints will be reflected
/// in all linked entities.
struct IkarusBlueprint; struct IkarusBlueprint;
/// \brief Creates a blueprint. /// \brief Flags for creating a blueprint.
/// \param project The project the blueprint is part of. enum IkarusBlueprintCreateFlags {
/// \brief No flags.
IkarusBlueprintCreateFlags_None = 0,
};
/// \brief Creates a new blueprint.
/// \param project The project to create the blueprint in.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param name The name of the blueprint. /// \param name The name of the blueprint.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \param error_out \see errors.h /// \pre \li Must not be empty.
/// \param flags Flags for creating the blueprint.
/// \return The created blueprint or null if an error occurs. /// \return The created blueprint or null if an error occurs.
/// \param error_out \see errors.h
/// \remark Ownership remains with libikarus.
IKA_API IkarusBlueprint * ikarus_blueprint_create( IKA_API IkarusBlueprint * ikarus_blueprint_create(
struct IkarusProject * project, struct IkarusProject * project,
char const * name, char const * name,
IkarusErrorData * error_out IkarusBlueprintCreateFlags flags,
struct IkarusErrorData * error_out
); );
/// \brief Deletes a blueprint. /// \brief Flags for creating a blueprint from an entity.
enum IkarusBlueprintCreateFromEntityFlags {
/// \brief No flags.
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.
/// \details Each value of the entity will be copied into the blueprint as a property.
/// \param entity The entity to create the blueprint from.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param name The name of the blueprint.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param flags Flags for creating the blueprint.
/// \param error_out \see errors.h
/// \return The created blueprint or null if an error occurs.
IKA_API IkarusBlueprint * ikarus_blueprint_create_from_entity(
struct IkarusEntity * entity,
char const * name,
IkarusBlueprintCreateFromEntityFlags flags,
struct IkarusErrorData * error_out
);
/// \brief Flags for copying a blueprint.
enum IkarusBlueprintCopyFlags {
/// \brief No flags.
IkarusBlueprintCopyFlags_None = 0,
};
/// \brief Copies a blueprint.
/// \param blueprint The blueprint to copy.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param flags Flags for copying the blueprint.
/// \param error_out \see errors.h
/// \return The copied blueprint or null if an error occurs.
IKA_API struct IkarusBlueprint * ikarus_blueprint_copy(
struct IkarusBlueprint * blueprint,
IkarusBlueprintCopyFlags flags,
struct IkarusErrorData * error_out
);
/// \brief Flags for deleting a blueprint.
enum IkarusBlueprintDeleteFlags {
/// \brief No flags.
IkarusBlueprintDeleteFlags_None = 0,
};
/// \brief Deletes a blueprint, all associated properties, and their values.
/// \param blueprint The blueprint to delete. /// \param blueprint The blueprint to delete.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param flags Flags for deleting the blueprint.
/// \remark Must not be used after deletion.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \remark The blueprint must not be accessed after deletion.
IKA_API void ikarus_blueprint_delete( IKA_API void ikarus_blueprint_delete(
IkarusBlueprint * blueprint, struct IkarusBlueprint * blueprint,
IkarusErrorData * error_out IkarusBlueprintDeleteFlags flags,
IkarusErrorData * error_out
); );
/// \brief Gets the ID of a blueprint. /// \brief Gets the project a blueprint belongs to.
/// \param blueprint The blueprint to get the ID of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The ID of the blueprint or 0 if an error occurs.
IKA_API int64_t ikarus_blueprint_get_id(
IkarusBlueprint const * blueprint,
IkarusErrorData * error_out
);
/// \brief Gets the project a blueprint is part of.
/// \param blueprint The blueprint to get the project of. /// \param blueprint The blueprint to get the project of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The project or null if an error occurs. /// \return The project the blueprint belongs to.
IKA_API IkarusProject * ikarus_blueprint_get_project( /// \remark Ownership remains with libikarus.
IkarusBlueprint const * blueprint, IKA_API struct IkarusProject * ikarus_blueprint_get_project(
IkarusErrorData * error_out struct IkarusBlueprint * blueprint,
struct IkarusErrorData * error_out
); );
/// \brief Gets the name of a blueprint. /// \brief Gets the name of a blueprint.
@ -71,79 +127,84 @@ IKA_API IkarusProject * ikarus_blueprint_get_project(
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The name or null if an error occurs. /// \return The name of the blueprint.
/// \remark Ownership remains with libikarus.
IKA_API char const * ikarus_blueprint_get_name( IKA_API char const * ikarus_blueprint_get_name(
IkarusBlueprint const * blueprint, struct IkarusBlueprint * blueprint,
IkarusErrorData * error_out struct IkarusErrorData * error_out
); );
/// \brief Flags for setting the name of a blueprint.
enum IkarusBlueprintSetNameFlags {
/// \brief No flags.
IkarusBlueprintSetNameFlags_None = 0,
};
/// \brief Sets the name of a blueprint. /// \brief Sets the name of a blueprint.
/// \param blueprint The blueprint to set the name of. /// \param blueprint The blueprint to set the name of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param name The new name of the blueprint. /// \param name The new name of the blueprint.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param flags Flags for setting the name of the blueprint.
/// \param error_out \see errors.h /// \param error_out \see errors.h
IKA_API void ikarus_blueprint_set_name( IKA_API void ikarus_blueprint_set_name(
IkarusBlueprint * blueprint, struct IkarusBlueprint * blueprint,
char const * name, char const * name,
IkarusErrorData * error_out IkarusBlueprintSetNameFlags flags,
struct IkarusErrorData * error_out
); );
/// \brief Gets the properties of a blueprint. /// \brief Gets the properties of a blueprint.
/// \param blueprint The blueprint to get the properties of. /// \param blueprint The blueprint to get the properties of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param properties_out The buffer to write the properties to. /// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs.
/// \pre \li Must not be null.
/// \param properties_out_size The size of the buffer.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \see ikarus_blueprint_get_property_count /// \return The properties of the blueprint or null if an error occurs.
IKA_API void ikarus_blueprint_get_properties( IKA_API struct IkarusProperty ** ikarus_blueprint_get_properties(
IkarusBlueprint const * blueprint, struct IkarusBlueprint * blueprint,
struct IkarusProperty ** properties_out, size_t * size_out,
size_t properties_out_size, struct IkarusErrorData * error_out
IkarusErrorData * error_out
); );
/// \brief Gets the number of properties of a blueprint. /// \brief Gets the number of properties of a blueprint.
/// \param blueprint The blueprint to get the number of properties of. /// \param blueprint The blueprint to get the properties count of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \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 or undefined 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_property_count( IKA_API size_t ikarus_blueprint_get_properties_count(
IkarusBlueprint const * blueprint, struct IkarusBlueprint * blueprint,
IkarusErrorData * error_out struct IkarusErrorData * error_out
); );
/// \brief Gets the entities linked to a blueprint. /// \brief Gets all entities linked to a blueprint.
/// \param blueprint The blueprint to get the linked entities of. /// \param blueprint The blueprint to get the entities of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param entities_out The buffer to write the entities to. /// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs.
/// \pre \li Must not be null. /// \remark Ignore if null.
/// \param entities_out_size The size of the buffer.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \see ikarus_blueprint_get_linked_entity_count /// \return The entities linked to the blueprint or null if an error occurs.
IKA_API void ikarus_blueprint_get_linked_entities( IKA_API struct IkarusEntity ** ikarus_blueprint_get_entities(
IkarusBlueprint const * blueprint, struct IkarusBlueprint * blueprint,
struct IkarusEntity ** entities_out, size_t * size_out,
size_t entities_out_size, struct IkarusErrorData * error_out
IkarusErrorData * error_out
); );
/// \brief Gets the number of entities linked to a blueprint. /// \brief Gets the number of entities linked to a blueprint.
/// \param blueprint The blueprint to get the number of linked entities of. /// \param blueprint The blueprint to get the entities count of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The number of linked entities or undefined 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_linked_entity_count( IKA_API size_t ikarus_blueprint_get_entities_count(
IkarusBlueprint const * blueprint, struct IkarusBlueprint * blueprint,
IkarusErrorData * error_out struct IkarusErrorData * error_out
); );
IKARUS_END_HEADER IKARUS_END_HEADER
// @} /// @}

View file

@ -5,7 +5,7 @@
#include <ikarus/stdtypes.h> #include <ikarus/stdtypes.h>
/// \file entity.h /// \file entity.h
/// \author Folling <folling@ikarus.world> /// \author Folling <mail@folling.io>
/// \defgroup entities Entities /// \defgroup entities Entities
/// \brief Entities are the core building blocks of Ikarus. /// \brief Entities are the core building blocks of Ikarus.
@ -13,15 +13,13 @@
IKARUS_BEGIN_HEADER IKARUS_BEGIN_HEADER
/// \brief Entities are the core building blocks of Ikarus. /// \brief Entities are the core building blocks of Ikarus.
/// \details Entities store two data: A name and a set of values. /// \details Entities store a number of values associated with a name or property.
/// The name identifies the entity but does not have to be unique. The values
/// are the actual information of the entity.
/// ///
/// For documentation on the types and layout of values \see Values.h. /// For documentation on the types and layout of values \see values.h.
/// ///
/// Entities can be linked to blueprints. /// Entities can be linked to blueprints.
/// The entity has a (possibly uninitialized) value for each property of all /// The entity has a (possibly uninitialized) value for each property of all
/// blueprints it is linked to. \see Property.h \see Blueprint.h. /// blueprints it is linked to. \see property.h \see blueprint.h.
/// ///
/// We distinguish between `EntityValues` and `EntityPropertyValues`. /// We distinguish between `EntityValues` and `EntityPropertyValues`.
/// `EntityValues` are a direct part of the entity. /// `EntityValues` are a direct part of the entity.
@ -29,59 +27,89 @@ IKARUS_BEGIN_HEADER
/// via a blueprint. /// via a blueprint.
struct IkarusEntity; struct IkarusEntity;
/// \brief Creates an entity. /// \brief Flags for creating an entity.
/// \param project The project the entity is part of. enum IkarusEntityCreateFlags {
/// \brief No flags.
IkarusEntityCreateFlags_None = 0,
};
/// \brief Creates a new entity.
/// \param project The project to create the entity in.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param name The name of the entity. /// \param name The name of the entity.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param flags Flags for creating the entity.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The created entity or null if an error occurs. /// \return The created entity or null if an error occurs.
IKA_API IkarusEntity * ikarus_entity_create( IKA_API IkarusEntity * ikarus_entity_create(
struct IkarusProject * project, struct IkarusProject * project,
char const * name, char const * name,
IkarusErrorData * error_out IkarusEntityCreateFlags flags,
IkarusErrorData * error_out
); );
/// \brief Deletes an entity. /// \brief Flags for deleting an entity.
enum IkarusEntityDeleteFlags {
/// \brief No flags.
IkarusEntityDeleteFlags_None = 0,
};
/// \brief Deletes an entity and its values.
/// \param entity The entity to delete. /// \param entity The entity to delete.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param flags Flags for deleting the entity.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \remark The entity must not be accessed after deletion. IKA_API void ikarus_entity_delete(
IKA_API void IkarusEntity * entity,
ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); IkarusEntityDeleteFlags flags,
IkarusErrorData * error_out
);
/// \brief Gets the id of an entity. /// \brief Flags for copying an entity.
/// \param entity The entity to get the id of. enum IkarusEntityCopyFlags {
/// \brief No flags.
IkarusEntityCopyFlags_None = 0,
};
/// \brief Copies an entity.
/// \param entity The entity to copy.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param flags Flags for copying the entity.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The id of the entity or 0 if an error occurs. /// \return The copied entity or null if an error occurs.
IKA_API int64_t IKA_API IkarusEntity * ikarus_entity_copy(
ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out); IkarusEntity * entity,
IkarusEntityCopyFlags flags,
IkarusErrorData * error_out
);
/// \brief Gets the project an entity is part of. /// \brief Gets the project an entity belongs to.
/// \param entity The entity to get the project of. /// \param entity The entity to get the project of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The project the entity is part of or null if an error occurs. /// \return The project the entity belongs to.
IKA_API IkarusProject * ikarus_entity_get_project( IKA_API struct IkarusProject *
IkarusEntity const * entity, ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out);
IkarusErrorData * error_out
);
/// \brief Gets the name of an entity. /// \brief Gets the name of an entity.
/// \param entity The entity to get the name of. /// \param entity The entity to get the name of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The name of the entity or null if an error occurs. /// \return The name of the entity.
IKA_API char const * ikarus_entity_get_name( IKA_API char const *
IkarusEntity const * entity, ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out);
IkarusErrorData * error_out
); /// \brief Flags for setting the name of an entity.
enum IkarusEntitySetNameFlags {
/// \brief No flags.
IkarusEntitySetNameFlags_None = 0,
};
/// \brief Sets the name of an entity. /// \brief Sets the name of an entity.
/// \param entity The entity to set the name of. /// \param entity The entity to set the name of.
@ -89,73 +117,27 @@ IKA_API char const * ikarus_entity_get_name(
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param name The new name of the entity. /// \param name The new name of the entity.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param flags Flags for setting the name of the entity.
/// \param error_out \see errors.h /// \param error_out \see errors.h
IKA_API void ikarus_entity_set_name( IKA_API void ikarus_entity_set_name(
IkarusEntity * entity, IkarusEntity * entity,
char const * name, char const * name,
IkarusErrorData * error_out IkarusEntitySetNameFlags flags,
IkarusErrorData * error_out
); );
/// \brief Checks if an entity is linked to a blueprint. /// \brief Gets the blueprints an entity is linked to.
/// \param entity The entity to check.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \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,
IkarusErrorData * error_out
);
/// \brief Links an entity to a blueprint.
/// \param entity The entity to link.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \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,
IkarusErrorData * error_out
);
/// \brief Unlinks an entity from a blueprint.
/// All values of the blueprints' properties will be deleted.
/// \param entity The entity to unlink.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \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,
IkarusErrorData * error_out
);
/// \brief Gets all 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.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param blueprints_out The buffer to write the blueprints to. /// \param size_out An out parameter for the number of blueprints in the returned array
/// \pre \li Must not be null. /// or undefined if an error occurs.
/// \param blueprints_out_size The size of the buffer.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \see ikarus_entity_get_linked_blueprint_count IKA_API struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints(
IKA_API void ikarus_entity_get_linked_blueprints( IkarusEntity * entity,
IkarusEntity const * entity, size_t * size_out,
struct IkarusBlueprint ** blueprints_out, IkarusErrorData * error_out
size_t blueprints_out_size,
IkarusErrorData * error_out
); );
/// \brief Gets the number of blueprints an entity is linked to. /// \brief Gets the number of blueprints an entity is linked to.
@ -163,156 +145,199 @@ IKA_API void ikarus_entity_get_linked_blueprints(
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The number of blueprints or undefined if an error occurs. /// \return The number of linked blueprints or 0 if an error occurs.
IKA_API size_t ikarus_entity_get_linked_blueprint_count( IKA_API size_t ikarus_entity_get_linked_blueprints_count(
IkarusEntity const * entity, IkarusEntity * entity,
IkarusErrorData * error_out IkarusErrorData * error_out
); );
/// \brief Checks if an entity has a value with a given name. /// \brief Flags for linking an entity to a blueprint.
/// \param entity The entity to check. enum IkarusEntityLinkBlueprintFlags {
/// \brief No flags.
IkarusEntityLinkBlueprintFlags_None = 0,
};
/// \brief Links an entity to a blueprint.
/// \details An uninitialized (default) value is created for each property of the
/// blueprint.
/// \param entity The entity to link the blueprint to.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param name The name of the value to check. /// \param blueprint The blueprint to link to.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist.
/// \pre \li Must be in the same project as the entity.
/// \remark If the entity is already linked to the blueprint, nothing happens.
/// \param flags Flags for linking the entity to the blueprint.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return False if the entity does not have a value associated with IKA_API void ikarus_entity_link_blueprint(
/// the name or if an error occurs, true otherwise. IkarusEntity * entity,
IKA_API bool ikarus_entity_has_value( struct IkarusBlueprint * blueprint,
IkarusEntity const * entity, IkarusEntityLinkBlueprintFlags flags,
char const * name, IkarusErrorData * error_out
IkarusErrorData * error_out
); );
/// \brief Gets the value of an entity. /// \brief Flags for unlinking an entity from a blueprint.
enum IkarusEntityUnlinkBlueprintFlags {
/// \brief No flags.
IkarusEntityUnlinkBlueprintFlags_None = 0,
/// \brief Keep the values associated with the blueprint, transforming them into entity values.
IkarusEntityUnlinkBlueprintFlags_KeepValues = 1,
};
/// \brief Unlinks an entity from a blueprint.
/// \details All values associated with the blueprint are deleted.
/// \param entity The entity to unlink the blueprint from.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param blueprint The blueprint to unlink from.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \remark If the entity is not linked to the blueprint, nothing happens.
/// \param flags Flags for unlinking the entity from the blueprint.
/// \param error_out \see errors.h
IKA_API void ikarus_entity_unlink_blueprint(
IkarusEntity * entity,
struct IkarusBlueprint * blueprint,
IkarusEntityUnlinkBlueprintFlags flags,
IkarusErrorData * error_out
);
/// \brief Gets the values of an entity.
/// \param entity The entity to get the values of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The values, in json format of or null if an error occurs.
/// The json representation is an array of objects with the following keys:
/// - `name`: The name of the value.
/// - `value`: The value itself. \see value.h
IKA_API char const *
ikarus_entity_get_values(IkarusEntity * entity, IkarusErrorData * error_out);
/// \brief Gets a value of an entity.
/// \param entity The entity to get the value of. /// \param entity The entity to get the value of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param name The name of the value to get. /// \param name The value's name.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must be an existing value of the entity. /// \remark Ownership remains with the client.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The value of the entity or null if an error occurs. /// \return The value, in json format of or null if an error occurs. \see value.h
IKA_API struct IkarusValue * ikarus_entity_get_value( IKA_API char const * ikarus_entity_get_value(
IkarusEntity const * entity, IkarusEntity * entity,
char const * name, char const * name,
IkarusErrorData * error_out IkarusErrorData * error_out
); );
/// \brief Sets the value of an entity. /// \brief Flags for setting a value of an entity.
/// If the entity does not have a value associated with the name, it is created. enum IkarusEntitySetValueFlags {
/// \remark Types are overwritten if the value already exists. /// \brief No flags.
IkarusEntitySetValueFlags_None = 0,
};
/// \brief Sets a value of an entity.
/// \details If no value exists for the name, a new one is created.
/// Any previous value is overwritten.
/// \param entity The entity to set the value of. /// \param entity The entity to set the value of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param name The name of the value to set. /// \param name The value's name.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \param value The value to set.
/// \param value The new value of the entity.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must be a valid json representation of a value. \see value.h
/// \param flags Flags for setting the value.
/// \param error_out \see errors.h /// \param error_out \see errors.h
IKA_API void ikarus_entity_set_value( IKA_API void ikarus_entity_set_value(
IkarusEntity * entity, IkarusEntity * entity,
char const * name, char const * name,
struct IkarusValue const * value, char const * value,
IkarusErrorData * error_out IkarusEntitySetValueFlags flags,
IkarusErrorData * error_out
); );
/// \brief Removes a value from an entity. /// \brief Flags for deleting a value of an entity.
/// \pre \li The value must exist. enum IkarusEntityDeleteValueFlags {
/// \brief No flags.
IkarusEntityDeleteValueFlags_None = 0,
};
/// \brief Deletes a value of an entity.
/// \param entity The entity to delete the value of. /// \param entity The entity to delete the value of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param name The name of the value to delete. /// \param name The property's name to delete the value of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must be the name of a value which exists within the property.
/// \param flags Flags for deleting the value.
/// \param error_out \see errors.h /// \param error_out \see errors.h
IKA_API void ikarus_entity_remove_value( IKA_API void ikarus_entity_delete_value(
IkarusEntity * entity, IkarusEntity * entity,
char const * name, char const * name,
IkarusErrorData * error_out IkarusEntityDeleteValueFlags flags,
IkarusErrorData * error_out
); );
/// \brief Checks if a property is linked to an entity. /// \brief Gets the property values of an entity.
/// \param entity The entity to check. /// \param entity The entity to get the property values of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param property The property to check.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return False if an error occurs or the entity does not have the property, /// \return The property values, in msgpack format of or null if an error occurs.
/// true otherwise. /// The format is a map of property pointers (as integers) to values. \see
IKA_API bool ikarus_entity_has_property( /// value.h
IkarusEntity const * entity, IKA_API char const * ikarus_entity_get_property_values(
struct IkarusProperty const * property, IkarusEntity * entity,
IkarusErrorData * error_out IkarusErrorData * error_out
);
/// \brief Gets the properties an entity is linked to.
/// \param entity The entity to get the linked properties of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param properties_out The buffer to write the linked properties to.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \param properties_out_size The size of the buffer.
/// \see ikarus_entity_get_property_count
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 number of properties an entity is linked to.
/// \param entity The entity to get the number of linked 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,
IkarusErrorData * error_out
); );
/// \brief Gets the value of a property of an entity. /// \brief Gets the value of a property of an entity.
/// \details If the entity has never set the value of the property, /// \param entity The entity to get the value of.
/// the property's default value is returned. /// \pre \li Must not be null.
/// \param entity The entity to get the value of. /// \pre \li Must exist.
/// \pre \li Must not be null. /// \param property The property to get the value of.
/// \pre \li Must exist. /// \pre \li Must not be null.
/// \param property The property to get the value of. /// \pre \li Must exist.
/// \pre \li Must not be null. /// \pre \li Must be linked to the entity.
/// \pre \li Must exist. /// \param error_out \see errors.h
/// \pre \li Must be linked to the entity. /// \return The value, in json format of or null if an error occurs. \see
/// \param error_out \see errors.h /// value.h
/// \return The value of the property or null if an error occurs. IKA_API char const * ikarus_entity_get_property_value(
IKA_API struct IkarusValue * ikarus_entity_get_property_value( IkarusEntity * entity,
IkarusEntity const * entity, struct IkarusProperty * property,
struct IkarusProperty const * property, IkarusErrorData * error_out
IkarusErrorData * error_out
); );
/// \brief Flags for setting the value of a property of an entity.
enum IkarusEntitySetPropertyValueFlags {
/// \brief No flags.
IkarusEntitySetPropertyValueFlags_None = 0,
};
/// \brief Sets the value of a property of an entity. /// \brief Sets the value of a property of an entity.
/// \param entity The entity to set the property value of. /// \details Any previous value is overwritten.
/// \param entity The entity to set the value of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param property The property to set the value of. /// \param property The property to set the value of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param value The new value of the property. /// \pre \li Must be linked to the entity.
/// \param value The value to set.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must be of the same type as the property. /// \pre \li Must be a valid json representation of a value. \see value.h
/// \pre \li Must be valid for the property's settings. /// \pre \li Must be valid for the property's schema. \see schema.h
/// \param flags Flags for setting the property value.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \remark If the entity does not have the property, this function fails.
IKA_API void ikarus_entity_set_property_value( IKA_API void ikarus_entity_set_property_value(
IkarusEntity * entity, IkarusEntity * entity,
struct IkarusProperty const * property, struct IkarusProperty * property,
struct IkarusValue const * value, char const * value,
IkarusErrorData * error_out IkarusEntitySetPropertyValueFlags flags,
IkarusErrorData * error_out
); );
IKARUS_END_HEADER IKARUS_END_HEADER
// @} /// @}

View file

@ -1,40 +0,0 @@
#pragma once
/// \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 value that can be either true or false. (e.g. "Is the character dead?")
/// @{
IKARUS_BEGIN_HEADER
struct IkarusNumberProperty;
/// \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.
/// \param property_source The property source to create the property for.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param default_value The default value for the property.
/// \pre \li Must not be null.
/// \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,
struct IkarusNumberValue * default_value,
IkarusErrorData * error_out
);
IKARUS_END_HEADER
/// @}

View file

@ -1,144 +0,0 @@
#pragma once
/// \file property.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/stdtypes.h>
#include <ikarus/values/value_cardinality.h>
#include <ikarus/values/value_type.h>
/// \defgroup properties Properties
/// \brief Properties define the structure and types of data.
/// @{
IKARUS_BEGIN_HEADER
/// \brief Properties define the structure of blueprints.
/// \details Each blueprint can have any number of properties.
/// Every property has a type that identifies the kind of data that can be put in.
/// This is the "base class" of properties. See the derived types (e.g. IkarusToggleProperty) for more information.
///
/// The following types currently exist:
/// - Toggle: A true/false boolean-like value
/// - Number: An arbitrary numeric value
/// - Text: An arbitrary textual value
///
/// Property Examples:
/// - Is Dead (Toggle)
/// - Age (Number)
/// - ISBN (Text)
///
/// Each entity associated with the property has a value for it.
///
/// \remark Values for properties are lazily created to save space.
/// Fetching the value for some property of some entity will return the property's default value if none is specified.
struct IkarusProperty;
/// \brief Deletes a property.
/// \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, IkarusErrorData * error_out);
/// \brief Gets the ID of a property.
/// \param property The property to get the ID of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The ID of the property or 0 if an error occurs.
IKA_API int64_t ikarus_property_get_id(IkarusProperty const * property, IkarusErrorData * error_out);
/// \brief Gets the project of a property.
/// \param property The property to get the project of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The project of the property or null if an error occurs.
IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty const * property, IkarusErrorData * error_out);
/// \brief Gets the name of an property.
/// \param entity The property 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 property or null if an error occurs.
IKA_API char const * ikarus_property_get_name(IkarusProperty const * entity, IkarusErrorData * error_out);
/// \brief Sets the name of an property.
/// \param entity The property to set the name of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param name The new name of the property.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
IKA_API void ikarus_property_set_name(IkarusProperty * entity, char const * name, 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 Changing the type of a property is not supported. This is because the property would need to change
IKA_API IkarusValueType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out);
// there is no `set_type` as we encode type information in the underlying datatype
/// \briefs Gets a property's cardinality.
/// \param property The property to get the cardinality of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The cardinality of the property or false if an error occurs.
IKA_API IkarusValueCardinality ikarus_property_get_cardinality(IkarusProperty const * property, IkarusErrorData * error_out);
/// \briefs Sets a property's default value.
/// \param property The property to set the cardinality of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \\param cardinality The new cardinality of the property.
/// \param error_out \see errors.h
IKA_API void
ikarus_property_set_cardinality(IkarusProperty * property, IkarusValueCardinality cardinality, IkarusErrorData * error_out);
/// \briefs Gets a property's default value.
/// \param property The property to get the default value 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.
IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out);
/// \brief Gets the source blueprint 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.
IKA_API struct IkarusBlueprint * ikarus_property_get_blueprint(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.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param toggle_property_visitor The function to call if the property is a toggle property. Skipped if null.
/// \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,
IkarusErrorData * error_out
);
IKARUS_END_HEADER
// @}

View file

@ -1,40 +0,0 @@
#pragma once
/// \file text_property.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
/// \addtogroup properties Properties
/// \brief Text properties store a value that can be either true or false. (e.g. "Is the character dead?")
/// @{
IKARUS_BEGIN_HEADER
struct IkarusTextProperty;
/// \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.
/// \param property_source The property source to create the property for.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param default_value The default value for the property.
/// \pre \li Must not be null.
/// \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,
struct IkarusTextValue * default_value,
IkarusErrorData * error_out
);
IKARUS_END_HEADER
/// @}

View file

@ -1,37 +0,0 @@
#pragma once
/// \file toggle_property.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
/// \addtogroup properties Properties
/// @{
IKARUS_BEGIN_HEADER
/// \brief Toggle properties store a value that can be either true or false. (e.g. "Is the character dead?")
struct IkarusToggleProperty;
/// \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.
/// \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
);
IKARUS_END_HEADER
/// @}

View file

@ -0,0 +1,143 @@
#pragma once
/// \file property.h
/// \author Folling <mail@folling.io>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/stdtypes.h>
/// \defgroup properties Properties
/// \brief Properties define the structure and types of data.
/// @{
IKARUS_BEGIN_HEADER
/// \brief Properties define the structure of blueprints.
/// \details Each blueprint can have any number of properties.
/// Every property has a type that identifies the kind of data that can be put
/// in. This is the "base class" of properties. See the derived types (e.g.
/// IkarusToggleProperty) for more information.
///
/// The following types currently exist:
/// - Toggle: A true/false boolean-like value
/// - Number: An arbitrary numeric value
/// - Text: An arbitrary textual value
///
/// Property Examples:
/// - Is Dead (Toggle)
/// - Age (Number)
/// - ISBN (Text)
///
/// Each entity associated with the property has a value for it.
///
/// \remark Values for properties are lazily created to save space.
/// Fetching the value for some property of some entity will return the
/// property's default value if none is specified.
struct IkarusProperty;
/// \brief Flags for creating a property.
enum IkarusPropertyCreateFlags {
/// \brief No flags.
IkarusPropertyCreateFlags_None = 0,
};
/// \brief Create a new 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 schema The schema of the property.
/// \pre \li Must not be null.
/// \param flags Flags for creating the property.
/// \param error_out \see errors.h
/// \return The created property or NULL if an error occurred.
/// \remark Must only be deleted with #ikarus_property_delete.
IKA_API IkarusProperty * ikarus_property_create(
struct IkarusProject * project,
char const * name,
struct IkarusValueSchema * schema,
IkarusPropertyCreateFlags flags,
IkarusErrorData * error_out
);
/// \brief Flags for deleting a property.
enum IkarusPropertyDeleteFlags {
/// \brief No flags.
IkarusPropertyDeleteFlags_None = 0,
};
/// \brief Delete a property.
/// \param property The property to delete.
/// \param flags Flags for deleting the property.
/// \param error_out \see errors.h
IKA_API void ikarus_property_delete(
IkarusProperty * property,
IkarusPropertyDeleteFlags flags,
IkarusErrorData * error_out
);
/// \brief Get the project a property belongs to.
/// \param property The property to get the project of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The project the property belongs to or null if an error occurred.
/// \remark Ownership remains with libikarus.
IKA_API struct IkarusProject * ikarus_property_get_project(
IkarusProperty * property,
IkarusErrorData * error_out
);
/// \brief Get the name of a property.
/// \param property The property 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 property or null if an error occurred.
/// \remark Ownership remains with libikarus.
IKA_API char const * ikarus_property_get_name(
IkarusProperty * property,
IkarusErrorData * error_out
);
/// \brief Get the schema of a property.
/// \param property The property to get the schema of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The schema of the property or null if an error occurred.
/// \remark Ownership remains with libikarus.
IKA_API struct IkarusValueSchema * ikarus_property_get_schema(
IkarusProperty * property,
IkarusErrorData * error_out
);
/// \brief Flags for setting the name of a property.
enum IkarusPropertySetNameFlags {
/// \brief No flags.
IkarusPropertySetNameFlags_None = 0,
};
/// \brief Set the name of a property.
/// \param property The property to set the name of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param name The new name of the property.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param flags Flags for setting the name of the property.
/// \param error_out \see errors.h
/// \remark Ownership remains with the caller.
IKA_API void ikarus_property_set_name(
IkarusProperty * property,
char const * name,
IkarusPropertySetNameFlags flags,
IkarusErrorData * error_out
);
IKARUS_END_HEADER
/// @}

View file

@ -1,7 +1,9 @@
#pragma once #pragma once
/// \file project.h /// \file project.h
/// \author Folling <folling@ikarus.world> /// \author Folling <mail@folling.io>
#include <cstdint>
#include <ikarus/errors.h> #include <ikarus/errors.h>
#include <ikarus/macros.h> #include <ikarus/macros.h>
@ -15,10 +17,19 @@
IKARUS_BEGIN_HEADER IKARUS_BEGIN_HEADER
/// \brief An Ikarus project. /// \brief An Ikarus project.
/// \details A project may only be open once at a time. Opening a project from two different locations Gets undefined /// \details A project may only be open once at a time.
/// Opening a project from two different locations results in undefined
/// behavior. /// behavior.
struct IkarusProject; struct IkarusProject;
/// \brief Flags for creating a project.
enum IkarusProjectCreateFlags {
/// \brief No flags.
IkarusProjectCreateFlags_None = 0,
/// \brief Allow overwriting existing files.
IkarusProjectCreateFlags_AllowOverwrite = 1 << 0,
};
/// \brief Creates a persisted project on the filesystem. /// \brief Creates a persisted project on the filesystem.
/// \param path The path to the project. /// \param path The path to the project.
/// \pre \li Must not be null. /// \pre \li Must not be null.
@ -26,126 +37,168 @@ struct IkarusProject;
/// \param name The name of the project. Must neither be null nor empty. /// \param name The name of the project. Must neither be null nor empty.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must not be empty. /// \pre \li Must not be empty.
/// \param flags Flags for creating the project.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The created project or null if an error occurs. /// \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 /// \remark Must be closed with #ikarus_project_close.
/// ikarus_project_delete IKA_API struct IkarusProject * ikarus_project_create(
IKA_API IkarusProject * ikarus_project_create( char const * path,
char const * path, char const * name,
char const * name, IkarusProjectCreateFlags flags,
IkarusErrorData * error_out IkarusErrorData * error_out
); );
/// \brief Creates a project in memory. /// \brief Flags for creating a project in memory.
enum IkarusProjectCreateInMemoryFlags {
/// \brief No flags.
IkarusProjectCreateInMemoryFlags_None = 0,
};
/// \brief Creates a project in memory. The project is not persisted.
/// \param name The name of the project. Must neither be null nor empty. /// \param name The name of the project. Must neither be null nor empty.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must not be empty. /// \pre \li Must not be empty.
/// \param flags Flags for creating the project.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The created project or null if an error occurs. /// \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 /// \remark Must be closed with #ikarus_project_close.
/// ikarus_project_delete IKA_API struct IkarusProject * ikarus_project_create_in_memory(
IKA_API IkarusProject * char const * name,
ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out); IkarusProjectCreateInMemoryFlags flags,
IkarusErrorData * error_out
);
/// \brief Flags for opening a project.
enum IkarusProjectOpenFlags {
/// \brief No flags.
IkarusProjectOpenFlags_None = 0,
};
/// \brief Opens an existing project. /// \brief Opens an existing project.
/// \param path The path to the project. /// \param path The path to the project.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must point to an existing project on the system. /// \pre \li Must point to an existing project on the system.
/// \param flags Flags for opening the project.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The opened project or null if an error occurs. /// \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 /// \remark Must be closed with #ikarus_project_close.
/// ikarus_project_delete IKA_API struct IkarusProject * ikarus_project_open(
IKA_API IkarusProject * char const * path,
ikarus_project_open(char const * path, IkarusErrorData * error_out); IkarusProjectOpenFlags flags,
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 Ownership remains with libikarus, must not be freed.
IKA_API char const * ikarus_project_get_name(
IkarusProject const * project,
IkarusErrorData * error_out
); );
/// \brief Sets the name of a project. /// \brief Flags for closing a project in memory.
/// \param project The project to set the name of. enum IkarusProjectCloseFlags {
/// \brief No flags.
IkarusProjectCloseFlags_None = 0,
};
/// \brief Closes a project. This function must be called to free resources.
/// \param project The project to close.
/// \pre \li Must not be null.
/// \pre \li Must be open.
/// \param flags Flags for closing the project.
/// \param error_out \see errors.h
/// \remark The project must not be used after closing.
/// \remark Does not delete the project from the filesystem.
/// \remark Mutually exclusive with #ikarus_project_delete.
IKA_API void ikarus_project_close(
struct IkarusProject * project,
IkarusProjectCloseFlags flags,
IkarusErrorData * error_out
);
/// \brief Flags for deleting a project.
enum IkarusProjectDeleteFlags {
/// \brief No flags.
IkarusProjectDeleteFlags_None = 0,
};
/// \brief Deletes a project from the filesystem.
/// \param project The project to delete.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param new_name The new name of the project. /// \param flags Flags for deleting the project.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param error_out \see errors.h /// \param error_out \see errors.h
IKA_API void ikarus_project_set_name( /// \remark The project must not be used after deletion.
IkarusProject * project, /// \remark Mutually exclusive with #ikarus_project_close.
char const * new_name, IKA_API void ikarus_project_delete(
IkarusErrorData * error_out struct IkarusProject * project,
IkarusProjectDeleteFlags flags,
IkarusErrorData * error_out
);
/// \brief Gets the name of a project.
/// \param project The project to delete.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \returns The name of the project or null if an error occurs.
IKA_API char const * ikarus_project_get_name(
struct IkarusProject const * project,
IkarusErrorData * error_out
); );
/// \brief Gets the path of a project. /// \brief Gets the path of a project.
/// \param project The project to get the path of. /// \param project The project to delete.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The path of the project. /// \returns The path of the project or null if an error occurs.
/// \remark Ownership remains with libikarus, must not be freed.
IKA_API char const * ikarus_project_get_path( IKA_API char const * ikarus_project_get_path(
IkarusProject const * project, struct IkarusProject const * project,
IkarusErrorData * error_out IkarusErrorData * error_out
); );
/// \brief Gets the entities of a project. /// \brief Gets all entities in a project.
/// \param project The project to get the entities of. /// \param project The project from which to get the entities.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param entities_out The buffer to write the entities to. /// \param size_out An out parameter for the number of entities in the returned array
/// \pre \li Must not be null. /// or undefined if an error occurs.
/// \param entities_out_size The size of the buffer.
/// \param error_out \see errors.h /// \param error_out \see errors.h
IKA_API void ikarus_project_get_entities( /// \returns An array of entities or null if an error occurs.
IkarusProject * project, IKA_API struct IkarusEntity ** ikarus_project_get_entities(
struct IkarusEntity ** entities_out, struct IkarusProject const * project,
size_t entities_out_size, size_t * size_out,
IkarusErrorData * error_out IkarusErrorData * error_out
); );
/// \brief Gets the number of entities of a project. /// \brief Gets how many entities in a project there are.
/// \param project The project to get the number of entities of. /// \param project The project from which to get the count.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \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 or undefined if an error occurs. /// \return The count or 0 if an error occurs.
IKA_API size_t ikarus_project_get_entity_count( IKA_API size_t ikarus_project_get_entities_count(
IkarusProject const * project, IkarusProject const * project,
IkarusErrorData * error_out IkarusErrorData * error_out
); );
/// \brief Gets the blueprints of a project. /// \brief Gets all blueprints from a project.
/// \param project The project to get the blueprints of. /// \param project The project from which to get the blueprints.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param blueprints_out The buffer to write the blueprints to. /// \param size_out An out parameter for the number of blueprints in the returned array
/// \pre \li Must not be null. /// or undefined if an error occurs.
/// \param blueprints_out_size The size of the buffer.
/// \param error_out \see errors.h /// \param error_out \see errors.h
IKA_API void ikarus_project_get_blueprints( /// \return An array of blueprints or null if an error occurs.
IkarusProject * project, IKA_API struct IkarusBlueprint ** ikarus_project_get_blueprints(
struct IkarusBlueprint ** blueprints_out, struct IkarusProject const * project,
size_t blueprints_out_size, size_t * size_out,
IkarusErrorData * error_out IkarusErrorData * error_out
); );
/// \brief Gets the number of blueprints of a project. /// \brief Gets how many blueprints in a project there are.
/// \param project The project to get the number of blueprints of. /// \param project The project from which to get the count.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The number of blueprints or undefined if an error occurs. /// \return The count or 0 if an error occurs.
IKA_API size_t ikarus_project_get_blueprint_count( IKA_API size_t ikarus_project_get_blueprints_count(
IkarusProject const * project, IkarusProject const * project,
IkarusErrorData * error_out IkarusErrorData * error_out
); );
IKARUS_END_HEADER IKARUS_END_HEADER

View file

@ -1,10 +1,11 @@
#pragma once #pragma once
#ifdef __cplusplus #ifdef __cplusplus
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
using std::size_t; using std::size_t;
#else #else
#include <stddef.h> #include <stdbool.h>
#include <stdint.h> #include <stddef.h>
#include <stdint.h>
#endif #endif

View file

@ -0,0 +1,48 @@
#pragma once
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/stdtypes.h>
/// \file data.h
/// \author Folling <mail@folling.io>
/// \addtogroup values Values
IKARUS_BEGIN_HEADER
/// \brief Data stores the actual information of a value.
/// \details Data is schemaless and can store any kind of data. Only when the
/// data is combined with a schema does it become a value. \see value.h.
/// Given the complexity of data, they are transferred as json.
/// The json representation of a data is a map with the following keys:
/// - `type` The type of the data. \see IkarusValueDataType. Must be one of the
/// following:
/// - `Primitive` A primitive value. Has two additional key:
/// - `primitive` The type of the primitive. Must be one of the following:
/// - `data` The stored data. Must be either a bool, double, or string.
/// - `Constant` A constant value. Has no additional keys, as the constant
/// value is shared across all values.
/// - `List` A list of values. Has the following additional keys:
/// - `data` An array of stored data.
/// - `Map` A map of key-value pairs. Has the following additional keys:
/// - `data` An array of key-value pairs.
/// - `Tuple` A tuple of values. Has the following additional keys:
/// - `data` An array of stored data.
/// Note that each sub-data is also a data, allowing for arbitrarily nested data
/// structures.
struct IkarusValueData;
/// \brief The type of data.
enum IkarusValueDataType {
/// \brief A primitive value. \see IkarusValuePrimitiveType.
IkarusValueDataType_Primitive = 1,
/// \brief A list of values.
IkarusValueDataType_List = 2,
/// \brief A map of key-value pairs.
IkarusValueDataType_Map = 3,
/// \brief A tuple of values.
IkarusValueDataType_Tuple = 4,
};
IKARUS_END_HEADER

View file

@ -1,109 +0,0 @@
#pragma once
/// \file number_value.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/values/value_cardinality.h>
/// \addtogroup values Values
/// @{
IKARUS_BEGIN_HEADER
/// \brief A numeric value. For example "Age" or "Height".
struct IkarusNumberValue;
/// \brief Creates an empty number value.
/// \details If the cardinality is "Single", the value will be initialized with 0.0.
/// \param cardinality The cardinality of the value.
/// \param error_out \see errors.h
/// \return The value or null if an error occurs.
IKA_API IkarusNumberValue * ikarus_number_value_create(IkarusValueCardinality cardinality, 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 NaN if an error occurs.
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.
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.
/// \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.
/// \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 Inserts a data into a number value.
/// \param value The number value.
/// \pre \li Must not be null.
/// \pre \li Cardinality must be "Multiple".
/// \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.
/// \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 Removes a data from a number value.
/// \param value The number value.
/// \pre \li Must not be null.
/// \pre \li Cardinality must be "Multiple".
/// \param idx The index of the data to remove.
/// \pre \li Must be less than the size of the value.
/// \param error_out \see errors.h
IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx, IkarusErrorData * error_out);
/// \brief Clears a number value.
/// \param value The number value.
/// \pre \li Cardinality must be "Multiple".
/// \param error_out \see errors.h
IKA_API void ikarus_number_value_clear(IkarusNumberValue * value, 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.
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, 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.
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 IkarusValueData * ikarus_number_value_to_value(IkarusNumberValue * value, IkarusErrorData * error_out);
IKARUS_END_HEADER
/// @}

View file

@ -0,0 +1,58 @@
#pragma once
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/stdtypes.h>
/// \file schema.h
/// \author Folling <mail@folling.io>
/// \addtogroup values Values
IKARUS_BEGIN_HEADER
/// \brief Schemas define the type of value.
/// \details Schemas are used to define the type of value. They are akin to
/// classes in programming languages.
/// Schemas are used to validate values and structure data.
///
/// Given the complexity of schemas, they are transferred as json.
/// The json representation of a schema is a map with the following keys:
/// - `type` The type of the schema. Must be one of the following:
/// - `Primitive` A primitive value. Has the following additional keys:
/// - `primitive` The type of the primitive value. \see IkarusPrimitiveType.
/// - `Constant` A constant value. Has the following additional keys:
/// - `value` The constant value, shared across all values of the schema.
/// \see value.h. \remark The schema is derived from the value.
/// - `List` A list of values. Has the following additional keys:
/// - `schema` The schema of the values in the list.
/// - `Map` A map of key-value pairs. Has the following additional keys:
/// - `key_schema` The schema of the keys.
/// - `value_schema` The schema of the values.
/// - `Tuple` A tuple of values. Has the following additional keys:
/// - `schemas` The schemas of the values in the tuple.
struct IkarusSchema;
/// \brief The type of primitive data.
enum IkarusValuePrimitiveType {
/// \brief A boolean.
IkarusValuePrimitiveType_Toggle = 1,
/// \brief A 64-bit floating point number.
IkarusValuePrimitiveType_Number = 2,
/// \brief An arbitrary length string.
IkarusValuePrimitiveType_Text = 3
};
/// \brief The type of schema.
enum IkarusValueSchemaType {
/// \brief A primitive value. \see IkarusPrimitiveType
IkarusValueSchemaType_Primitive = 1,
/// \brief A homogeneous list of values.
IkarusValueSchemaType_List = 2,
/// \brief A mapping from Value->Value.
IkarusValueSchemaType_Map = 3,
/// \brief A heterogeneous list of values.
IkarusValueSchemaType_Tuple = 4
};
IKARUS_END_HEADER

View file

@ -1,109 +0,0 @@
#pragma once
/// \file text_value.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/values/value_cardinality.h>
/// \addtogroup values Values
/// @{
IKARUS_BEGIN_HEADER
/// \brief A numeric value. For example "Age" or "Height".
struct IkarusTextValue;
/// \brief Creates an empty text value.
/// \details If the cardinality is "Single", the value will be initialized with 0.0.
/// \param cardinality The cardinality of the value.
/// \param error_out \see errors.h
/// \return The value or null if an error occurs.
IKA_API IkarusTextValue * ikarus_text_value_create(IkarusValueCardinality cardinality, IkarusErrorData * error_out);
/// \brief Fetches the underlying 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 fetch.
/// \pre \li Must be less than the size of the value.
/// \param error_out \see errors.h
/// \return The underlying data or NaN if an error occurs.
IKA_API char 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.
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.
/// \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 Inserts a data into a text value.
/// \param value The text value.
/// \pre \li Must not be null.
/// \pre \li Cardinality must be "Multiple".
/// \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.
/// \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 Removes a data from a text value.
/// \param value The text value.
/// \pre \li Must not be null.
/// \pre \li Cardinality must be "Multiple".
/// \param idx The index of the data to remove.
/// \pre \li Must be less than the size of the value.
/// \param error_out \see errors.h
IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx, IkarusErrorData * error_out);
/// \brief Clears a text value.
/// \param value The text value.
/// \pre \li Cardinality must be "Multiple".
/// \param error_out \see errors.h
IKA_API void ikarus_text_value_clear(IkarusTextValue * value, 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.
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, 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.
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 IkarusValueData * ikarus_text_value_to_value(IkarusTextValue * value, IkarusErrorData * error_out);
IKARUS_END_HEADER
/// @}

View file

@ -1,109 +0,0 @@
#pragma once
/// \file toggle_value.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/values/value_cardinality.h>
/// \addtogroup values Values
/// @{
IKARUS_BEGIN_HEADER
/// \brief A numeric value. For example "Age" or "Height".
struct IkarusToggleValue;
/// \brief Creates an empty toggle value.
/// \details If the cardinality is "Single", the value will be initialized with 0.0.
/// \param cardinality The cardinality of the value.
/// \param error_out \see errors.h
/// \return The value or null if an error occurs.
IKA_API IkarusToggleValue * ikarus_toggle_value_create(IkarusValueCardinality cardinality, IkarusErrorData * error_out);
/// \brief Fetches the underlying data of a toggle value at a specific index.
/// \param value The toggle 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 NaN if an error occurs.
IKA_API bool 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.
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.
/// \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.
/// \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 Inserts a data into a toggle value.
/// \param value The toggle value.
/// \pre \li Must not be null.
/// \pre \li Cardinality must be "Multiple".
/// \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.
/// \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 Removes a data from a toggle value.
/// \param value The toggle value.
/// \pre \li Must not be null.
/// \pre \li Cardinality must be "Multiple".
/// \param idx The index of the data to remove.
/// \pre \li Must be less than the size of the value.
/// \param error_out \see errors.h
IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx, IkarusErrorData * error_out);
/// \brief Clears a toggle value.
/// \param value The toggle value.
/// \pre \li Cardinality must be "Multiple".
/// \param error_out \see errors.h
IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value, 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.
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, 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.
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 IkarusValueData * ikarus_toggle_value_to_value(IkarusToggleValue * value, IkarusErrorData * error_out);
IKARUS_END_HEADER
/// @}

View file

@ -1,61 +1,26 @@
#pragma once #pragma once
/// \file value.h
/// \author Folling <folling@ikarus.world>
/// \defgroup values Values
/// \brief Values are data in entities.
/// \details An entity is made up of any number of values.
/// Each value defines a certain aspect of an entity.
/// Values have a name, a type, and some data.
/// Examples of values would be:
/// - Is Dead (Toggle)
/// - Age (Number)
/// - ISBN (Text)
///
/// Values are either single or multiple.
/// We call this property "Cardinality" (\see IkarusValueCardinality)
/// because it's really hard to find a simpler name.
/// Each piece of data within a value is called a "datapoint".
/// Single values have exactly one datapoint, multiple values have any number of
/// datapoints. For example "Age" would be singular, while "Nicknames" would be
/// multiple. The type is unaffected by this. A pendant in programming languages
/// would be a List<T>. Note that all values are stored as a list of items,
/// even if the value is singular. Single values effectively act as a list with
/// one element. This is enforced by the API at runtime.
///
/// For a comprehensive list of value types, see \ref IkarusValueType.
/// @{
#include <ikarus/errors.h> #include <ikarus/errors.h>
#include <ikarus/macros.h> #include <ikarus/macros.h>
#include <ikarus/stdtypes.h>
/// \file value.h
/// \author Folling <mail@folling.io>
/// \defgroup entities Entities
/// \brief Entities are the core building blocks of Ikarus.
IKARUS_BEGIN_HEADER IKARUS_BEGIN_HEADER
/// \brief The common type for all value data. /// \brief Values are data containers for entities.
struct IkarusValue; /// \details Values are flexible enough to store any kind of data. They are
/// akin to objects in programming languages.
/// \brief Visits an entity value, /// Each value has a schema that defines the type of the value. \see schema.h
/// calling the appropriate function for the value's type. /// They also store data appropriate for the schema.
/// \param value The entity value to visit. ///
/// \pre \li Must not be null. /// Given the complexity of values, they are transferred as json.
/// \param toggle_visitor The function to call if the value is a toggle value. /// The json representation of a value is a map with the following keys:
/// \remark Skipped if null. /// - `schema`: The schema of the value. \see schema.h.
/// \param number_visitor The function to call if the value is a number value. /// - `data`: The data of the value. \see data.h.
/// \remark Skipped if null.
/// \param text_visitor The function to call if the value is a text value.
/// \remark 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,
IkarusErrorData * error_out
);
IKARUS_END_HEADER IKARUS_END_HEADER
/// @}

View file

@ -1,23 +0,0 @@
#pragma once
/// \file value_cardinality.h
/// \author Folling <folling@ikarus.world>
/// \addtogroup values Values
/// @{
#include <ikarus/macros.h>
IKARUS_BEGIN_HEADER
/// \brief The cardinality of a value.
enum IkarusValueCardinality {
/// \brief Only contains one datapoint
IkarusValueCardinality_Single,
/// \brief Contains any number of datapoints
IkarusValueCardinality_List
};
IKARUS_END_HEADER
/// @}

View file

@ -1,198 +0,0 @@
#pragma once
/// \file value_schema.h
/// \author Folling <folling@ikarus.world>
/// \addtogroup values Values
/// @{
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/stdtypes.h>
IKARUS_BEGIN_HEADER
/// \brief Layouts define the structure and constraints of values.
/// \details Ikarus lets you define any schema for values and add constraints to them.
/// These schemas are assembled hierarchical and are akin to JSON schemas.
/// Layouts may be arbitrarily nested and combined e.g.:
/// Combination<List<Text>, Number, Complex<"Foo":Toggle,"Bar":Text>
struct IkarusValueLayout;
/// \brief A fixed datapoint with a fixed layout.
/// Example: Age: Union<Number, Constant<Text, "Dead">>
struct IkarusValueLayoutConstant;
/// \brief A singular datapoint with one of a list of layouts.
/// Example: ChestReward: Combination<Item, Gold, Mimic>
struct IkarusValueLayoutCombination;
/// \brief A collection of datapoints with a homogenous layout.
/// Example: Friends: List<Character>
struct IkarusValueLayoutList;
/// \brief A collection of nameable datapoints with heterogeneous layouts.
/// Example: GeoLocation: Complex<"Latitude":Number, "Longitude":Number>
struct IkarusValueLayoutComplex;
/// \brief Defines the type of datapoints.
enum IkarusValueDataType {
/// \brief Boolean datapoints
/// Example: Is the character alive? Yes
Toggle,
/// \brief Numeric datapoints
/// Example: How much does the character weigh? 57kg
Number,
/// \brief Textual datapoints
/// Example: What is the character's maiden name? Sandra
Text,
/// \brief A colour datapoint
/// Example: What colours make up the nation's flag? White/Pink/Blue.
/// \remark Not yet implemented
Colour,
/// \brief A date/time datapoint, interfacing with the calendar and timeline feature.
/// Example: When was the city founded? 12th of January 233 at 2:11
/// \remark Not yet implemented
Time,
/// \brief A location datapoint, interfacing with the map feature.
/// Example: Where is the city situated? 12.345, 67.890
/// \remark Not yet implemented
Location,
/// \brief An enum-esque datapoint
/// Example: Of which rarity is the weapon? Normal/Rare/Legendary
/// \remark Not yet implemented
Choice,
/// \brief A datapoint linking to some other object in Ikarus
/// Example: Who wrote this hymn? Peter Parker
/// \remark Not yet implemented
Reference
};
/// \brief Stores either a schema or a datatype
struct IkarusValueSchema;
/// \see ikarus_value_schema_from_layout_const
IkarusValueSchema * ikarus_value_schema_from_layout(
IkarusValueLayout * layout,
IkarusErrorData * error_out
);
/// \brief Creates a value schema from a layout.
/// \param layout The layout to create the schema from.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The created schema or null if an error occurs.
IkarusValueSchema const * ikarus_value_schema_from_layout_const(
IkarusValueLayout const * layout,
IkarusErrorData * error_out
);
/// \brief Creates a value schema from a datatype.
/// \param type The datatype to create the schema from.
/// \param error_out \see errors.h
/// \return The created schema or null if an error occurs.
IkarusValueSchema * ikarus_value_schema_from_data_type(
IkarusValueDataType type,
IkarusErrorData * error_out
);
/// \brief Frees a value schema.
/// \param schema The schema to free.
/// \pre \li Must not be null.
/// \remark Must not be accessed after freeing.
void ikarus_value_schema_free(IkarusValueSchema * schema);
/// \see ikarus_value_schema_visit_const
void ikarus_value_schema_visit(
IkarusValueSchema * schema,
void (*layout_visitor)(IkarusValueLayout * layout),
void (*data_type_visitor)(IkarusValueDataType type),
IkarusErrorData * error_out
);
/// \brief Visits a value schema.
/// \param schema The schema to visit.
/// \pre \li Must not be null.
/// \param layout_visitor The function to call if the schema is a layout. Skipped if null.
/// \param data_type_visitor The function to call if the schema is a datatype. Skipped if null.
void ikarus_value_schema_visit_const(
IkarusValueSchema const * schema,
void (*layout_visitor)(IkarusValueLayout const * layout),
void (*data_type_visitor)(IkarusValueDataType type),
IkarusErrorData * error_out
);
/// \brief Creates a constant layout.
/// \param schema The schema of the value.
/// \pre \li Must not be null.
/// \param value The value of the constant.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
IkarusValueLayoutConstant * ikarus_value_layout_constant_create(
IkarusValueSchema * schema,
struct IkarusValue * value,
IkarusErrorData * error_out
);
/// \brief Gets the underyling schema of a constant layout.
/// \param layout The layout to get the underyling schema of.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The underlying schema of the layout or null if an error occurs.
IkarusValueSchema const * ikarus_value_layout_constant_get_underyling_schema(
IkarusValueLayoutConstant const * layout,
IkarusErrorData * error_out
);
/// \brief Gets the underyling value of a constant layout.
/// \param layout The layout to get the underyling value of.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The underlying value of the layout or null if an error occurs.
struct IkarusValue const * ikarus_value_layout_constant_get_underlying_value(
IkarusValueLayoutConstant const * layout,
IkarusErrorData * error_out
);
/// \brief Creates a combination layout.
/// \param schemas The schemas of the values.
/// \pre \li Must not be null.
/// \param schemas_size The number of schemas.
/// \param error_out \see errors.h
/// \return The created layout or null if an error occurs.
/// \remark The schemas are copied.
IkarusValueLayoutCombination * ikarus_value_layout_combination_create(
IkarusValueSchema * schemas,
size_t schemas_size,
IkarusErrorData * error_out
);
void ikarus_value_layout_combination_get_schemas(
IkarusValueLayoutCombination const * layout,
IkarusValueSchema ** schemas_out,
size_t * schemas_size_out,
IkarusErrorData * error_out
);
size_t ikarus_value_layout_combination_get_schemas_count(
IkarusValueLayoutCombination const * layout,
IkarusErrorData * error_out
);
IkarusValueLayoutList * ikarus_value_layout_list_create(
IkarusValueSchema * schema,
IkarusErrorData * error_out
);
IkarusValueLayoutComplex * ikarus_value_layout_complex_create(
IkarusValueSchema * schemas,
size_t schemas_size,
IkarusErrorData * error_out
);
IkarusValueLayoutComplex * ikarus_value_layout_complex_create_named(
IkarusValueSchema * schemas,
char const ** names,
size_t schemas_size,
IkarusErrorData * error_out
);
IKARUS_END_HEADER
/// @}

View file

@ -1,6 +0,0 @@
module ikarus {
header "ikarus/persistence/project.h"
header "ikarus/objects/entity.h"
header "ikarus/objects/blueprint.h"
export *
}

View file

@ -7,4 +7,4 @@ file(
"*.c" "*.c"
) )
set(SOURCE_FILES ${FILES} PARENT_SCOPE) set(SOURCE_FILES ${FILES} PARENT_SCOPE)

View file

@ -1,41 +1,72 @@
#include "ikarus/errors.h" #include "ikarus/errors.h"
#include <cstddef>
#include <ikarus/errors.hpp>
void safe_strcpy(
std::string_view const src,
char * dest,
std::size_t const dest_size
) {
int i = 0;
for (; i < dest_size - 1; ++i) {
if (src[i] == '\0') {
break;
}
dest[i] = src[i];
}
dest[i] = '\0';
}
char const * ikarus_get_error_info_name(IkarusErrorInfo info) { char const * ikarus_get_error_info_name(IkarusErrorInfo info) {
switch (info) { switch (info) {
case IkarusErrorInfo_None: return "None"; case IkarusErrorInfo_None: return "None";
case IkarusErrorInfo_Client_Misuse: return "Client::Misuse"; case IkarusErrorInfo_Client_Misuse: return "Client::Misuse";
case IkarusErrorInfo_Client_InvalidInput: return "Client::InvalidInput"; case IkarusErrorInfo_Client_InvalidInput: return "Client::InvalidInput";
case IkarusErrorInfo_Client_InvalidFormat: return "Client::InvalidFormat"; case IkarusErrorInfo_Client_NonExistent: return "Client::NonExistent";
case IkarusErrorInfo_Client_ConstraintViolated: return "Client::ConstraintViolated"; case IkarusErrorInfo_Client_InvalidFormat: return "Client::InvalidFormat";
case IkarusErrorInfo_Client_ConstraintViolated:
return "Client::ConstraintViolated";
case IkarusErrorInfo_Filesystem_NotFound: return "Filesystem::NotFound"; case IkarusErrorInfo_Filesystem_NotFound: return "Filesystem::NotFound";
case IkarusErrorInfo_Filesystem_AlreadyExists: return "Filesystem::AlreadyExists"; case IkarusErrorInfo_Filesystem_AlreadyExists:
case IkarusErrorInfo_Filesystem_MissingPermissions: return "Filesystem::MissingPermissions"; return "Filesystem::AlreadyExists";
case IkarusErrorInfo_Filesystem_InsufficientSpace: return "Filesystem::InsufficientSpace"; case IkarusErrorInfo_Filesystem_MissingPermissions:
case IkarusErrorInfo_Filesystem_InvalidPath: return "Filesystem::InvalidPath"; return "Filesystem::MissingPermissions";
case IkarusErrorInfo_Filesystem_InsufficientSpace:
return "Filesystem::InsufficientSpace";
case IkarusErrorInfo_Filesystem_InvalidPath:
return "Filesystem::InvalidPath";
case IkarusErrorInfo_Database_ConnectionFailed: return "Database::ConnectionFailed"; case IkarusErrorInfo_Database_ConnectionFailed:
case IkarusErrorInfo_Database_QueryFailed: return "Database::QueryFailed"; return "Database::ConnectionFailed";
case IkarusErrorInfo_Database_MigrationFailed: return "Database::MigrationFailed"; case IkarusErrorInfo_Database_QueryFailed: return "Database::QueryFailed";
case IkarusErrorInfo_Database_InvalidState: return "Database::InvalidState"; case IkarusErrorInfo_Database_MigrationFailed:
return "Database::MigrationFailed";
case IkarusErrorInfo_Database_InvalidState: return "Database::InvalidState";
case IkarusErrorInfo_OS_SystemCallFailed: return "OS::SystemCallFailed"; case IkarusErrorInfo_OS_SystemCallFailed: return "OS::SystemCallFailed";
case IkarusErrorInfo_OS_InvalidReturnValue: return "OS::InvalidReturnValue"; case IkarusErrorInfo_OS_InvalidReturnValue: return "OS::InvalidReturnValue";
case IkarusErrorInfo_OS_InsufficientMemory: return "OS::InsufficientMemory"; case IkarusErrorInfo_OS_InsufficientMemory: return "OS::InsufficientMemory";
case IkarusErrorInfo_LibIkarus_InvalidState: return "LibIkarus::InvalidState"; case IkarusErrorInfo_LibIkarus_InvalidState:
case IkarusErrorInfo_LibIkarus_CannotPerformOperation: return "LibIkarus::CannotPerformOperation"; return "LibIkarus::InvalidState";
case IkarusErrorInfo_LibIkarus_Timeout: return "LibIkarus::Timeout"; case IkarusErrorInfo_LibIkarus_CannotPerformOperation:
return "LibIkarus::CannotPerformOperation";
case IkarusErrorInfo_LibIkarus_Timeout: return "LibIkarus::Timeout";
default: return "Invalid"; default: return "Invalid";
} }
} }
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->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->info != IkarusErrorInfo_None;
} }

View file

@ -1,80 +1,169 @@
#pragma once #pragma once
#include <string_view> #include <string_view>
#include <type_traits>
#include <ikarus/errors.h> #include <ikarus/errors.h>
inline void safe_strcpy(char * dest, std::string_view src, size_t dest_size) { void safe_strcpy(
for (int i = 0; i < dest_size; ++i) { std::string_view const src,
if (src[i] == '\0') { char * dest,
dest[i] = '\0'; size_t const dest_size
return; );
}
dest[i] = src[i]; #define IKARUS_SET_ERROR(msg, err_info) \
} if (error_out != nullptr) { \
} safe_strcpy( \
msg, \
#define IKARUS_SET_ERROR(msg, err_info) \ static_cast<char *>(error_out->message), \
if (error_out != nullptr) { \ IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT \
safe_strcpy(static_cast<char *>(error_out->message), msg, IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT); \ ); \
error_out->info = err_info; \ error_out->info = err_info; \
} }
#define IKARUS_FAIL(ret, msg, err_info) \ #define IKARUS_FAIL(ret, msg, err_info) \
IKARUS_SET_ERROR(msg, err_info); \ IKARUS_SET_ERROR(msg, err_info); \
return ret return ret
#define IKARUS_FAIL_IF(condition, ret, msg, err_info) \ #define IKARUS_FAIL_IF(condition, ret, msg, err_info) \
if (condition) { \ if (condition) { \
IKARUS_SET_ERROR(msg, err_info) \ IKARUS_SET_ERROR(msg, err_info) \
return ret; \ return ret; \
} }
#define IKARUS_FAIL_IF_ERROR(ret) \ #define IKARUS_TRY_OR_FAIL_IMPL(var_name, msg, err_info, ...) \
if (ikarus_error_data_is_error(error_out)) { \ auto var_name = __VA_ARGS__; \
return ret; \ if (var_name.is_error()) { \
} IKARUS_SET_ERROR( \
fmt::format( \
fmt::runtime(msg), \
std::move(var_name).unwrap_error() \
), \
err_info \
); \
return var_name; \
}
#define IKARUS_FAIL_IF_NULL(ptr, ret) IKARUS_FAIL_IF(((ptr) == nullptr), ret, #ptr " must not be null", IkarusErrorInfo_Client_InvalidNull) #define IKARUS_TRY_OR_FAIL(msg, err_info, ...) \
IKARUS_TRY_OR_FAIL_IMPL( \
CPPBASE_UNIQUE_NAME(result), \
msg, \
err_info, \
__VA_ARGS__ \
);
#define IKARUS_TRY_OR_FAIL_IMPL(var_name, 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(fmt::format(msg, var_name.unwrap_error()), err_info); \ IKARUS_SET_ERROR( \
return var_name; \ fmt::format( \
} fmt::runtime(msg), \
std::move(var_name).unwrap_error() \
#define IKARUS_TRY_OR_FAIL(msg, err_info, ...) IKARUS_TRY_OR_FAIL_IMPL(CPPBASE_UNIQUE_NAME(result), msg, err_info, __VA_ARGS__); ), \
err_info \
#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ ); \
auto var_name = __VA_ARGS__; \ return ret; \
if (var_name.is_error()) { \ }
IKARUS_SET_ERROR(fmt::format(msg, 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(CPPBASE_UNIQUE_NAME(result), ret, msg, err_info, __VA_ARGS__); IKARUS_TRYRV_OR_FAIL_IMPL( \
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(fmt::format(msg, var_name.unwrap_error()), err_info); \ IKARUS_SET_ERROR( \
return var_name; \ fmt::format( \
} \ fmt::runtime(msg), \
value = var_name.unwrap_value() std::move(var_name).unwrap_error() \
), \
err_info \
); \
return var_name; \
} \
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(CPPBASE_UNIQUE_NAME(result), value, msg, err_info, __VA_ARGS__); IKARUS_VTRY_OR_FAIL_IMPL( \
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(fmt::format(msg, var_name.unwrap_error()), err_info); \ IKARUS_SET_ERROR( \
return ret; \ fmt::format(fmt::runtime(msg), var_name.unwrap_error()), \
} \ err_info \
value = var_name.unwrap_value() ); \
return ret; \
} \
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(CPPBASE_UNIQUE_NAME(result), value, ret, msg, err_info, __VA_ARGS__); IKARUS_VTRYRV_OR_FAIL_IMPL( \
CPPBASE_UNIQUE_NAME(result), \
value, \
ret, \
msg, \
err_info, \
__VA_ARGS__ \
);
#define IKARUS_FAIL_IF_ERROR(ret) \
if (ikarus_error_data_is_error(error_out)) { \
return ret; \
}
#define IKARUS_FAIL_IF_NULL(ptr, ret) \
IKARUS_FAIL_IF( \
((ptr) == nullptr), \
ret, \
#ptr " must not be null", \
IkarusErrorInfo_Client_InvalidNull \
)
#define IKARUS_FAIL_IF_NAME_INVALID(name, ret) \
IKARUS_FAIL_IF_NULL(name, ret); \
IKARUS_FAIL_IF( \
cppbase::is_empty_or_blank(name), \
ret, \
#name " must not be empty", \
IkarusErrorInfo_Client_InvalidInput \
);
#define IKARUS_FAIL_IF_NOT_EXIST(object, ret) \
IKARUS_VTRYRV_OR_FAIL( \
auto exists, \
ret, \
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, \
ret, \
fmt::format( \
"{} doesn't exist", \
std::remove_cvref_t<decltype(*object)>::object_name \
), \
IkarusErrorInfo_Client_NonExistent \
)

View file

@ -1,168 +1,5 @@
#include "ikarus/objects/blueprint.h" #include "ikarus/objects/blueprint.h"
#include <cppbase/logger.hpp>
#include <cppbase/result.hpp>
#include <cppbase/strings.hpp>
#include <ikarus/errors.hpp> #include <ikarus/errors.hpp>
#include <ikarus/objects/blueprint.hpp> #include <ikarus/objects/blueprint.hpp>
#include <ikarus/objects/entity.hpp>
#include <ikarus/objects/properties/property.h>
#include <ikarus/objects/properties/property.hpp>
#include <ikarus/objects/util.hpp>
#include <ikarus/persistence/project.hpp> #include <ikarus/persistence/project.hpp>
#include <ikarus/values/value_type.h>
IkarusBlueprint::IkarusBlueprint(IkarusProject * project, int64_t id):
IkarusObject{project, id} {}
std::string_view IkarusBlueprint::get_table_name() const noexcept {
return "blueprints";
}
IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(project, nullptr);
IKARUS_FAIL_IF_NAME_INVALID(name, nullptr);
IKARUS_VTRYRV_OR_FAIL(
int64_t const id,
nullptr,
"failed to create blueprint: {}",
IkarusErrorInfo_Database_QueryFailed,
project->db->transact([name](auto * db) -> cppbase::Result<int64_t, sqlitecpp::TransactionError> {
TRY(db->execute("INSERT INTO `blueprints`(`name`) VALUES(?)", name));
return cppbase::ok(db->last_insert_rowid());
})
);
return project->get_blueprint(id);
}
void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(blueprint, );
IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, );
IKARUS_TRYRV_OR_FAIL(
,
"unable to delete blueprint: {}",
IkarusErrorInfo_Database_QueryFailed,
blueprint->project->db->execute("DELETE FROM `blueprints` WHERE `id` = ?", blueprint->id)
);
blueprint->project->uncache(blueprint);
}
int64_t ikarus_blueprint_get_id(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) {
return ikarus::util::object_get_id(blueprint, error_out);
}
IkarusProject * ikarus_blueprint_get_project(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) {
return ikarus::util::object_get_project(blueprint, error_out);
}
char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) {
return ikarus::util::object_get_name(blueprint, error_out);
}
void ikarus_blueprint_set_name(IkarusBlueprint * blueprint, char const * name, IkarusErrorData * error_out) {
ikarus::util::object_set_name(blueprint, name, error_out);
}
void ikarus_blueprint_get_properties(
IkarusBlueprint const * blueprint,
struct IkarusProperty ** properties_out,
size_t properties_out_size,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(blueprint, );
IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, );
IKARUS_FAIL_IF_NULL(properties_out, );
if (properties_out_size == 0) {
return;
}
std::tuple<int64_t, IkarusValueType> ids_and_types[properties_out_size];
IKARUS_TRYRV_OR_FAIL(
,
"unable to fetch blueprint properties from database: {}",
IkarusErrorInfo_Database_QueryFailed,
blueprint->project->db->query_many_buffered<int64_t, IkarusValueType>(
"SELECT `id` FROM `properties` WHERE `blueprint` = ?",
ids_and_types,
properties_out_size,
blueprint->id
)
)
for (size_t i = 0; i < properties_out_size; ++i) {
auto [id, type] = ids_and_types[i];
properties_out[i] = blueprint->project->get_property(id, type);
}
}
size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(blueprint, 0);
IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, 0);
IKARUS_VTRYRV_OR_FAIL(
auto const ret,
0,
"unable to fetch blueprint property count from database: {}",
IkarusErrorInfo_Database_QueryFailed,
blueprint->project->db->query_one<int64_t>("SELECT COUNT(*) FROM `properties` WHERE `blueprint` = ?", blueprint->id)
);
return ret;
}
void ikarus_blueprint_get_linked_entities(
IkarusBlueprint const * blueprint,
struct IkarusEntity ** entities_out,
size_t entities_out_size,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(blueprint, );
IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, );
IKARUS_FAIL_IF_NULL(entities_out, );
if (entities_out_size == 0) {
return;
}
int64_t ids[entities_out_size];
IKARUS_TRYRV_OR_FAIL(
,
"unable to fetch blueprint linked entities from database: {}",
IkarusErrorInfo_Database_QueryFailed,
blueprint->project->db->query_many_buffered<int64_t>(
"SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?",
ids,
entities_out_size,
blueprint->id
)
)
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, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(blueprint, 0);
IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, 0);
IKARUS_VTRYRV_OR_FAIL(
auto const ret,
0,
"unable to fetch blueprint linked entity count from database: {}",
IkarusErrorInfo_Database_QueryFailed,
blueprint->project->db
->query_one<int64_t>("SELECT COUNT(`entity`) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id)
);
return ret;
}

View file

@ -1,19 +1,23 @@
#pragma once #pragma once
#include <ikarus/objects/object.hpp> #include <string>
struct IkarusBlueprint : public IkarusObject { struct IkarusBlueprint {
public: consteval static inline auto OBJECT_NAME() -> std::string_view {
IkarusBlueprint(struct IkarusProject * project, int64_t id); return "blueprint";
}
IkarusBlueprint(IkarusBlueprint const &) = default; consteval static inline auto TABLE_NAME() -> std::string_view {
IkarusBlueprint(IkarusBlueprint &&) = default; return "blueprints";
}
IkarusBlueprint & operator=(IkarusBlueprint const &) = default; IkarusBlueprint(
IkarusBlueprint & operator=(IkarusBlueprint &&) = default; struct IkarusProject * project,
int64_t id,
std::string_view name
);
~IkarusBlueprint() override = default; struct IkarusProject * project;
int64_t id;
public: std::string name;
std::string_view get_table_name() const noexcept override;
}; };

View file

@ -1,488 +1,171 @@
#include "ikarus/objects/entity.h" #include "entity.hpp"
#include <cppbase/strings.hpp>
#include <ikarus/errors.h>
#include <ikarus/errors.hpp> #include <ikarus/errors.hpp>
#include <ikarus/objects/blueprint.hpp> #include <ikarus/objects/entity.h>
#include <ikarus/objects/entity.hpp>
#include <ikarus/objects/properties/property.hpp>
#include <ikarus/objects/util.hpp>
#include <ikarus/persistence/project.hpp> #include <ikarus/persistence/project.hpp>
#include <ikarus/values/entity_property_value.hpp>
#include <ikarus/values/value.hpp>
#include <ikarus/values/value_type.h>
IkarusEntity::IkarusEntity(IkarusProject * project, int64_t id): IkarusEntity::IkarusEntity(
IkarusObject{project, id} {} struct IkarusProject * project,
int64_t id,
std::string_view IkarusEntity::get_table_name() const noexcept { std::string_view name
return "entities"; ):
} project{project},
id{id},
name{name} {}
IkarusEntity * ikarus_entity_create( IkarusEntity * ikarus_entity_create(
struct IkarusProject * project, struct IkarusProject * project,
char const * name, char const * name,
IkarusErrorData * error_out IkarusEntityCreateFlags flags,
IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(project, nullptr); IKARUS_FAIL_IF_NULL(project, nullptr);
IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); IKARUS_FAIL_IF_NAME_INVALID(name, nullptr);
IKARUS_VTRYRV_OR_FAIL( IKARUS_TRYRV_OR_FAIL(
int64_t const id, nullptr,
nullptr, "failed to create entity: {}",
"failed to create entity: {}", IkarusErrorInfo_Database_QueryFailed,
IkarusErrorInfo_Database_QueryFailed, project->db->execute("INSERT INTO `entities`(`name`) VALUES(?)", name)
project->db->transact( );
[name](auto * db
) -> cppbase::Result<int64_t, sqlitecpp::TransactionError> {
TRY(db->execute(
"INSERT INTO `entities`(`name`) VALUES(?, ?)",
name
));
return cppbase::ok(db->last_insert_rowid());
}
)
);
return project->get_entity(id); auto const id = project->db->last_insert_rowid();
return new IkarusEntity{project, id, name};
} }
void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { void ikarus_entity_delete(
IKARUS_FAIL_IF_NULL(entity, ); IkarusEntity * entity,
IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); IkarusEntityDeleteFlags flags,
IkarusErrorData * error_out
IKARUS_TRYRV_OR_FAIL(
,
"unable to delete entity: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db
->execute("DELETE FROM `entities` WHERE `id` = ?", entity->id)
);
entity->project->uncache(entity);
}
int64_t
ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out) {
return ikarus::util::object_get_id(entity, error_out);
}
IkarusProject * ikarus_entity_get_project(
IkarusEntity const * entity,
IkarusErrorData * error_out
) { ) {
return ikarus::util::object_get_project(entity, error_out); IKARUS_FAIL_IF_NULL(entity, );
IKARUS_FAIL_IF_NOT_EXIST(entity, );
IKARUS_FAIL_IF_NULL(entity->project, );
IKARUS_TRYRV_OR_FAIL(
,
"failed to delete entity: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db
->execute("DELETE FROM `entities` WHERE `id` = ?", entity->id)
);
delete entity;
} }
char const * ikarus_entity_get_name( IkarusEntity * ikarus_entity_copy(
IkarusEntity const * entity, IkarusEntity * entity,
IkarusErrorData * error_out IkarusEntityCopyFlags flags,
IkarusErrorData * error_out
) { ) {
return ikarus::util::object_get_name(entity, error_out); IKARUS_FAIL_IF_NULL(entity, nullptr);
IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr);
IKARUS_FAIL_IF_NULL(entity->project, nullptr);
IKARUS_VTRYRV_OR_FAIL(
auto id,
nullptr,
"failed to copy entity: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->transact(
[entity](auto * db)
-> cppbase::Result<int64_t, sqlitecpp::TransactionError> {
TRY(entity->project->db->execute(
"INSERT INTO `entities`(`name`) VALUES(?)",
entity->name.data()
));
TRY(entity->project->db->execute(
"INSERT INTO `entity_values`(`entity`, `name`, `value`)"
" SELECT ?1, `name`, `value` FROM `entity_values` WHERE "
"`entity` = ?1",
entity->id
))
TRY(entity->project->db->execute(
"INSERT INTO `entity_property_values`("
" `entity`, "
" `property`,"
" `value`"
") "
"SELECT ?1, `property`, `value` FROM "
"`entity_property_values` "
"WHERE `entity` = ?1",
entity->id
))
TRY(entity->project->db->execute(
"INSERT INTO `entity_blueprint_links`(`entity`, "
"`blueprint`)"
"SELECT ?1, `property`, `value` FROM "
"`entity_property_values` "
"WHERE `entity` = ?1",
entity->id
))
return cppbase::ok(entity->project->db->last_insert_rowid());
}
)
);
return new IkarusEntity{entity->project, id, entity->name};
}
IkarusProject *
ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(entity, nullptr);
IKARUS_FAIL_IF_NULL(entity->project, nullptr);
return entity->project;
}
char const *
ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(entity, nullptr);
return entity->name.data();
} }
void ikarus_entity_set_name( void ikarus_entity_set_name(
IkarusEntity * entity, IkarusEntity * entity,
char const * name, char const * name,
IkarusErrorData * error_out IkarusEntitySetNameFlags flags,
IkarusErrorData * error_out
) { ) {
ikarus::util::object_set_name(entity, name, error_out); IKARUS_FAIL_IF_NULL(entity, );
IKARUS_FAIL_IF_NAME_INVALID(name, );
IKARUS_TRYRV_OR_FAIL(
,
"failed to set name for entity: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->execute(
"UPDATE `entities` SET `name` = ? WHERE `id` = ?",
name,
entity->id
)
);
entity->name = name;
} }
bool ikarus_entity_is_linked_to_blueprint( char const * ikarus_entity_get_value(
IkarusEntity const * entity, IkarusEntity * entity,
struct IkarusBlueprint const * blueprint, char const * name,
IkarusErrorData * error_out IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(entity, false); IKARUS_FAIL_IF_NULL(entity, nullptr);
IKARUS_FAIL_IF_OBJECT_MISSING(entity, false); IKARUS_FAIL_IF_NULL(name, nullptr);
IKARUS_FAIL_IF_NULL(blueprint, false);
IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, false);
IKARUS_VTRYRV_OR_FAIL(
auto const ret,
false,
"unable to check whether 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 ret;
}
void ikarus_entity_link_to_blueprint(
IkarusEntity * entity,
struct IkarusBlueprint * blueprint,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, );
IKARUS_FAIL_IF_OBJECT_MISSING(entity, );
IKARUS_FAIL_IF_NULL(blueprint, );
IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, );
IKARUS_TRYRV_OR_FAIL(
,
"unable to link entity to blueprint: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->execute(
"INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) "
"VALUES(?, ?) ON "
"CONFLICT(`entity`, `blueprint`) DO NOTHING",
entity->id,
blueprint->id
)
);
}
void ikarus_entity_unlink_from_blueprint(
IkarusEntity * entity,
struct IkarusBlueprint * blueprint,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, );
IKARUS_FAIL_IF_OBJECT_MISSING(entity, );
IKARUS_FAIL_IF_NULL(blueprint, );
IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, );
IKARUS_TRYRV_OR_FAIL(
,
"unable to unlink entity from blueprint: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->execute(
"DELETE FROM `entity_blueprint_links` WHERE `entity` = ? AND "
"`blueprint` = ?",
entity->id,
blueprint->id
)
);
IKARUS_TRYRV_OR_FAIL(
,
"unable to remove entity property values: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->execute(
"DELETE FROM `entity_property_values` WHERE `entity` = ? AND "
"`property` IN (SELECT "
"`id` FROM `properties` WHERE `blueprint` = ?)",
entity->id,
blueprint->id
)
)
}
void ikarus_entity_get_linked_blueprints(
IkarusEntity const * entity,
struct IkarusBlueprint ** blueprints_out,
size_t blueprints_out_size,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, );
IKARUS_FAIL_IF_OBJECT_MISSING(entity, );
IKARUS_FAIL_IF_NULL(blueprints_out, );
if (blueprints_out_size == 0) {
return;
}
int64_t ids[blueprints_out_size];
IKARUS_TRYRV_OR_FAIL(
,
"unable to fetch entity linked blueprints from database: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_many_buffered<int64_t>(
"SELECT `blueprint` FROM `entity_blueprint_links` WHERE `entity` = "
"?",
ids,
blueprints_out_size,
entity->id
)
)
for (size_t i = 0; i < blueprints_out_size; ++i) {
blueprints_out[i] = entity->project->get_blueprint(ids[i]);
}
}
size_t ikarus_entity_get_linked_blueprint_count(
IkarusEntity const * entity,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, 0);
IKARUS_FAIL_IF_OBJECT_MISSING(entity, 0);
IKARUS_VTRYRV_OR_FAIL(
auto const ret,
0,
"unable to fetch entity linked blueprint count from database: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_one<int64_t>(
"SELECT COUNT(`blueprint`) FROM `entity_blueprint_links` WHERE "
"`entity` = ?",
entity->id
)
);
return ret;
}
bool ikarus_entity_has_value(
IkarusEntity const * entity,
char const * name,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, false);
IKARUS_FAIL_IF_OBJECT_MISSING(entity, false);
IKARUS_FAIL_IF_NAME_INVALID(name, false);
IKARUS_VTRYRV_OR_FAIL(
auto const has_value,
false,
"unable to check whether 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 has_value;
}
struct IkarusValue * ikarus_entity_get_value(
IkarusEntity const * entity,
char const * name,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, nullptr);
IKARUS_FAIL_IF_OBJECT_MISSING(entity, nullptr);
IKARUS_FAIL_IF_VALUE_MISSING(entity, name, nullptr);
IKARUS_FAIL_IF_NAME_INVALID(name, nullptr);
auto * value = fetch_value_from_db(
entity->project,
error_out,
"SELECT `value` FROM `entity_values` WHERE `entity` = ? AND `name` = ?",
entity->id,
name
);
IKARUS_FAIL_IF_ERROR(nullptr);
return value;
} }
void ikarus_entity_set_value( void ikarus_entity_set_value(
IkarusEntity * entity, IkarusEntity * entity,
char const * name, char const * name,
struct IkarusValue const * value, char const * value,
IkarusErrorData * error_out IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(entity, ); IKARUS_FAIL_IF_NULL(entity, );
IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); IKARUS_FAIL_IF_NULL(name, );
IKARUS_FAIL_IF_NAME_INVALID(name, ); IKARUS_FAIL_IF_NULL(value, );
IKARUS_FAIL_IF_NULL(value, );
IKARUS_TRYRV_OR_FAIL(
,
"unable to set entity value: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->execute(
"INSERT INTO `entity_values`(`entity`, `name`, `value`) VALUES(?1, "
"?2, ?3) ON "
"CONFLICT(`entity`, `name`) DO UPDATE SET `value` = ?3",
entity->id,
name,
boost::json::serialize(value->to_json())
)
);
}
void ikarus_entity_delete_value(
IkarusEntity * entity,
char const * name,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, );
IKARUS_FAIL_IF_OBJECT_MISSING(entity, );
IKARUS_FAIL_IF_VALUE_MISSING(entity, name, );
IKARUS_FAIL_IF_NAME_INVALID(name, );
IKARUS_TRYRV_OR_FAIL(
,
"unable to delete entity value: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->execute(
"DELETE FROM `entity_values` WHERE `entity` = ? AND `name` = ?",
entity->id,
name
)
);
}
bool ikarus_entity_has_property(
IkarusEntity const * entity,
struct IkarusProperty const * property,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, false);
IKARUS_FAIL_IF_OBJECT_MISSING(entity, false);
IKARUS_FAIL_IF_NULL(property, false);
IKARUS_FAIL_IF_OBJECT_MISSING(property, false);
// given that values are loaded lazily we can't just check
// `entity_property_values` here
IKARUS_VTRYRV_OR_FAIL(
auto const has_property,
false,
"unable to check whether entity has property: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_one<bool>(
"SELECT EXISTS(\n"
" SELECT 1\n"
" FROM `entity_blueprint_links`\n"
" JOIN `properties` ON `properties`.`blueprint` = "
"`entity_blueprint_links`.`blueprint`\n"
" WHERE `entity_blueprint_links`.`entity` = ? AND "
"`properties`.`id` = ?\n"
")",
entity->id,
property->id
)
)
return has_property;
}
void ikarus_entity_get_properties(
IkarusEntity const * entity,
struct IkarusProperty ** properties_out,
size_t properties_out_size,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, );
IKARUS_FAIL_IF_OBJECT_MISSING(entity, );
IKARUS_FAIL_IF_NULL(properties_out, );
if (properties_out_size == 0) {
return;
}
std::tuple<int64_t, IkarusValueType> ids_and_types[properties_out_size];
// given that values are loaded lazily we can't just check
// `entity_property_values` here
IKARUS_TRYRV_OR_FAIL(
,
"unable to fetch properties from entity: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_many_buffered<int64_t, IkarusValueType>(
"SELECT `properties`.`id`, `properties`.`type`\n"
"FROM `entity_blueprint_links`\n"
"JOIN `properties` ON `properties`.`blueprint` = "
"`entity_blueprint_links`.`blueprint`\n"
"WHERE `entity_blueprint_links`.`entity` = ?\n",
ids_and_types,
properties_out_size,
entity->id
)
);
for (size_t i = 0; i < properties_out_size; ++i) {
auto [id, type] = ids_and_types[i];
properties_out[i] = entity->project->get_property(id, type);
}
}
size_t ikarus_entity_get_property_count(
IkarusEntity const * entity,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, 0);
IKARUS_FAIL_IF_OBJECT_MISSING(entity, 0);
// given that values are loaded lazily we can't just check
// `entity_property_values` here
IKARUS_VTRYRV_OR_FAIL(
size_t const count,
0,
"unable to fetch property count from entity: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_one<int64_t>(
"SELECT COUNT(`properties`.`id`)\n"
"FROM `entity_blueprint_links`\n"
"JOIN `properties` ON `properties`.`blueprint` = "
"`entity_blueprint_links`.`blueprint`\n"
"WHERE `entity_blueprint_links`.`entity` = ?\n"
")",
entity->id
)
);
return count;
}
struct IkarusValue * ikarus_entity_get_property_value(
IkarusEntity const * entity,
struct IkarusProperty const * property,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, nullptr);
IKARUS_FAIL_IF_OBJECT_MISSING(entity, nullptr);
IKARUS_FAIL_IF_NULL(property, nullptr);
IKARUS_FAIL_IF_OBJECT_MISSING(property, nullptr);
auto * value = fetch_value_from_db(
entity->project,
error_out,
"SELECT IFNULL(\n"
" (\n"
" SELECT `value`\n"
" FROM `entity_property_values`\n"
" WHERE `entity` = ?1 AND `property` = ?2\n"
" ),\n"
" (SELECT `default_value` FROM `properties` WHERE `id` = ?2)\n"
")",
entity->id,
property->id
);
IKARUS_FAIL_IF_ERROR(nullptr);
return value;
}
void ikarus_entity_set_property_value(
IkarusEntity * entity,
struct IkarusProperty const * property,
struct IkarusValue * value,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, );
IKARUS_FAIL_IF_OBJECT_MISSING(entity, );
IKARUS_FAIL_IF_NULL(property, );
IKARUS_FAIL_IF_OBJECT_MISSING(property, );
IKARUS_FAIL_IF_NULL(value, );
IKARUS_TRYRV_OR_FAIL(
,
"unable to set entity property value: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->execute(
"INSERT INTO `entity_property_values`(`entity`, `property`, "
"`value`) VALUES(?1, ?2, "
"?3) ON CONFLICT(`entity`, `property`) DO UPDATE SET `value` = ?3",
entity->id,
property->id,
boost::json::serialize(value->to_json())
)
);
} }

View file

@ -1,19 +1,24 @@
#pragma once #pragma once
#include <ikarus/objects/object.hpp> #include <string>
#include <vector>
struct IkarusEntity : public IkarusObject { #include <ikarus/values/value.hpp>
public:
inline IkarusEntity(struct IkarusProject * project, int64_t id);
IkarusEntity(IkarusEntity const &) = default; struct IkarusEntity {
IkarusEntity(IkarusEntity &&) = default; constinit static inline auto object_name = "entity";
constinit static inline auto table_name = "entities";
IkarusEntity & operator=(IkarusEntity const &) = default; IkarusEntity(
IkarusEntity & operator=(IkarusEntity &&) = default; struct IkarusProject * project,
int64_t id,
std::string_view name
);
~IkarusEntity() override = default; struct IkarusProject * project;
int64_t id;
std::string name;
public: std::vector<std::pair<std::string_view, IkarusValue *>> values_ordered;
std::string_view get_table_name() const noexcept override; std::unordered_map<std::string, IkarusValue> values;
}; };

View file

@ -1,7 +0,0 @@
#include "object.hpp"
#include <ikarus/persistence/project.hpp>
IkarusObject::IkarusObject(IkarusProject * project, int64_t id):
project{project},
id{id} {}

View file

@ -1,36 +0,0 @@
#pragma once
#include <string_view>
class IkarusObject {
public:
IkarusObject(struct IkarusProject * project, int64_t id);
IkarusObject(IkarusObject const &) = default;
IkarusObject(IkarusObject &&) = default;
IkarusObject & operator=(IkarusObject const &) = default;
IkarusObject & operator=(IkarusObject &&) = default;
virtual ~IkarusObject() = default;
public:
virtual std::string_view get_table_name() const noexcept = 0;
public:
struct IkarusProject * project;
int64_t id;
};
#define IKARUS_FAIL_IF_OBJECT_MISSING_IMPL(var_name, obj, ret) \
IKARUS_VTRYRV_OR_FAIL( \
bool const var_name, \
ret, \
"unable to check whether object exists: {}", \
IkarusErrorInfo_Database_QueryFailed, \
(obj)->project->db->template query_one<bool>("SELECT EXISTS(SELECT 1 FROM `objects` WHERE `id` = ?)", (obj)->id) \
) \
\
IKARUS_FAIL_IF(!(var_name), ret, "object does not exist", IkarusErrorInfo_Client_Misuse);
#define IKARUS_FAIL_IF_OBJECT_MISSING(obj, ret) IKARUS_FAIL_IF_OBJECT_MISSING_IMPL(CPPBASE_UNIQUE_NAME(exists), obj, ret);

View file

@ -1,32 +0,0 @@
#include "number_property.hpp"
#include <cppbase/result.hpp>
#include <ikarus/objects/properties/property.h>
#include <ikarus/objects/properties/property_scope.hpp>
#include <ikarus/objects/properties/util.hpp>
#include <ikarus/values/value.hpp>
IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, int64_t id):
IkarusProperty{project, id, this} {}
IkarusNumberProperty * ikarus_number_property_create(
struct IkarusProject * project,
char const * name,
struct IkarusPropertyScope * property_source,
IkarusErrorData * error_out
) {
return ikarus::util::create_property<IkarusNumberProperty>(project, name, property_source, error_out);
}
IkarusNumberValue * ikarus_number_property_get_default_value(IkarusNumberProperty * property, IkarusErrorData * error_out) {
return ikarus::util::get_default_value<IkarusNumberProperty>(property, error_out);
}
void ikarus_number_property_set_default_value(
IkarusNumberProperty * property,
IkarusNumberValue * new_default_value,
IkarusErrorData * error_out
) {
ikarus::util::set_default_value<IkarusNumberProperty>(property, new_default_value, error_out);
}

View file

@ -1,14 +0,0 @@
#pragma once
#include <ikarus/objects/properties/property.hpp>
#include <ikarus/values/number_value.hpp>
#include <ikarus/values/value_type.h>
struct IkarusNumberProperty : public IkarusProperty {
public:
using value_type = IkarusNumberValue;
constexpr auto static PropertyType = IkarusValueType_Number;
public:
IkarusNumberProperty(struct IkarusProject * project, int64_t id);
};

View file

@ -1,143 +0,0 @@
#include "property.hpp"
#include <cppbase/logger.hpp>
#include <ikarus/errors.hpp>
#include <ikarus/objects/properties/property.h>
#include <ikarus/objects/properties/property_scope.hpp>
#include <ikarus/values/value_type.h>
#include <ikarus/objects/util.hpp>
#include <ikarus/persistence/project.hpp>
#include <ikarus/values/value.hpp>
IkarusProperty::IkarusProperty(IkarusProject * project, int64_t id, Data data):
IkarusObject{project, id},
data{data} {}
IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(property, );
IKARUS_FAIL_IF_OBJECT_MISSING(property, );
IKARUS_TRYRV_OR_FAIL(
,
"unable to delete property: {}",
IkarusErrorInfo_Database_QueryFailed,
property->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", property->id)
);
property->project->uncache(property);
}
int64_t ikarus_property_get_id(IkarusProperty const * property, IkarusErrorData * error_out) {
return ikarus::util::object_get_id(property, error_out);
}
IkarusProject * ikarus_property_get_project(IkarusProperty const * property, IkarusErrorData * error_out) {
return ikarus::util::object_get_project(property, error_out);
}
char const * ikarus_property_get_name(IkarusProperty const * property, IkarusErrorData * error_out) {
return ikarus::util::object_get_name(property, error_out);
}
void ikarus_property_set_name(IkarusProperty * property, char const * name, IkarusErrorData * error_out) {
ikarus::util::object_set_name(property, name, error_out);
}
IkarusValueType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(property, IkarusValueType_Toggle);
IKARUS_FAIL_IF_OBJECT_MISSING(property, IkarusValueType_Toggle);
IKARUS_VTRYRV_OR_FAIL(
auto const ret,
IkarusValueType_Toggle,
"unable to fetch property type from database: {}",
IkarusErrorInfo_Database_QueryFailed,
property->project->db->query_one<int>("SELECT `type` FROM `properties` WHERE `id` = ?", property->id)
);
return static_cast<IkarusValueType>(ret);
}
IkarusPropertyScope const * ikarus_property_get_scope(IkarusProperty const * property, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(property, nullptr);
IKARUS_FAIL_IF_OBJECT_MISSING(property, nullptr);
IKARUS_VTRYRV_OR_FAIL(
auto const source,
nullptr,
"unable to fetch property source from database: {}",
IkarusErrorInfo_Database_QueryFailed,
property->project->db->query_one<int64_t>("SELECT `source` FROM `properties` WHERE `id` = ?", property->id)
);
switch (ikarus_id_get_object_type(source)) {
case IkarusObjectType_Blueprint: return new IkarusPropertyScope{property->project->get_blueprint(source)};
case IkarusObjectType_Entity: return new IkarusPropertyScope{property->project->get_entity(source)};
default:
IKARUS_FAIL(
nullptr,
fmt::format("invalid property source type: {}", ikarus_object_type_to_string(ikarus_id_get_object_type(source))),
IkarusErrorInfo_LibIkarus_InvalidState
);
}
}
IkarusValueData * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(property, nullptr);
IKARUS_FAIL_IF_OBJECT_MISSING(property, nullptr);
return fetch_value_from_db(property->project, error_out, "SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id);
}
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,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(property, );
IKARUS_FAIL_IF_OBJECT_MISSING(property, );
std::visit(
cppbase::overloaded{
[toggle_property_visitor, data](IkarusToggleProperty * property) { toggle_property_visitor(property, data); },
[number_property_visitor, data](IkarusNumberProperty * property) { number_property_visitor(property, data); },
[text_property_visitor, data](IkarusTextProperty * property) { text_property_visitor(property, data); }
},
property->data
);
}
void ikarus_property_visit_const(
IkarusProperty const * property,
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,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(property, );
IKARUS_FAIL_IF_OBJECT_MISSING(property, );
std::visit(
cppbase::overloaded{
[toggle_property_visitor, data](IkarusToggleProperty const * property) { toggle_property_visitor(property, data); },
[number_property_visitor, data](IkarusNumberProperty const * property) { number_property_visitor(property, data); },
[text_property_visitor, data](IkarusTextProperty const * property) { text_property_visitor(property, data); }
},
property->data
);
}
// No existence checks here for performance reasons. All methods on IkarusObject perform this check anyway.
IkarusObject * ikarus_property_to_object(IkarusProperty * property) {
return static_cast<IkarusObject *>(property);
}
IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property) {
return static_cast<IkarusObject const *>(property);
}

View file

@ -1,29 +0,0 @@
#pragma once
#include <variant>
#include <ikarus/objects/object.hpp>
struct IkarusProperty : public IkarusObject {
public:
using Data = std::variant<struct IkarusToggleProperty *, struct IkarusNumberProperty *, struct IkarusTextProperty *>;
public:
IkarusProperty(struct IkarusProject * project, int64_t id, Data data);
IkarusProperty(IkarusProperty const &) = default;
IkarusProperty(IkarusProperty &&) = default;
IkarusProperty & operator=(IkarusProperty const &) = default;
IkarusProperty & operator=(IkarusProperty &&) = default;
~IkarusProperty() override = default;
public:
inline std::string_view get_table_name() const noexcept override {
return "properties";
}
public:
Data data;
};

View file

@ -1,32 +0,0 @@
#include "text_property.hpp"
#include <cppbase/result.hpp>
#include <ikarus/objects/properties/property.h>
#include <ikarus/objects/properties/property_scope.hpp>
#include <ikarus/objects/properties/util.hpp>
#include <ikarus/values/value.hpp>
IkarusTextProperty::IkarusTextProperty(IkarusProject * project, int64_t id):
IkarusProperty{project, id, this} {}
IkarusTextProperty * ikarus_text_property_create(
struct IkarusProject * project,
char const * name,
struct IkarusPropertyScope * property_source,
IkarusErrorData * error_out
) {
return ikarus::util::create_property<IkarusTextProperty>(project, name, property_source, error_out);
}
IkarusTextValue * ikarus_text_property_get_default_value(IkarusTextProperty * property, IkarusErrorData * error_out) {
return ikarus::util::get_default_value<IkarusTextProperty>(property, error_out);
}
void ikarus_text_property_set_default_value(
IkarusTextProperty * property,
IkarusTextValue * new_default_value,
IkarusErrorData * error_out
) {
ikarus::util::set_default_value<IkarusTextProperty>(property, new_default_value, error_out);
}

View file

@ -1,14 +0,0 @@
#pragma once
#include <ikarus/objects/properties/property.hpp>
#include <ikarus/values/value_type.h>
#include <ikarus/values/text_value.hpp>
struct IkarusTextProperty : public IkarusProperty {
public:
using value_type = IkarusTextValue;
constexpr auto static PropertyType = IkarusValueType_Text;
public:
IkarusTextProperty(struct IkarusProject * project, int64_t id);
};

View file

@ -1,32 +0,0 @@
#include "toggle_property.hpp"
#include <cppbase/result.hpp>
#include <ikarus/objects/properties/property.h>
#include <ikarus/objects/properties/property_scope.hpp>
#include <ikarus/objects/properties/util.hpp>
#include <ikarus/values/value.hpp>
IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, int64_t id):
IkarusProperty{project, id, this} {}
IkarusToggleProperty * ikarus_toggle_property_create(
struct IkarusProject * project,
char const * name,
struct IkarusPropertyScope * property_source,
IkarusErrorData * error_out
) {
return ikarus::util::create_property<IkarusToggleProperty>(project, name, property_source, error_out);
}
IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, IkarusErrorData * error_out) {
return ikarus::util::get_default_value<IkarusToggleProperty>(property, error_out);
}
void ikarus_toggle_property_set_default_value(
struct IkarusToggleProperty * property,
struct IkarusToggleValue * new_default_value,
IkarusErrorData * error_out
) {
ikarus::util::set_default_value<IkarusToggleProperty>(property, new_default_value, error_out);
}

View file

@ -1,17 +0,0 @@
#pragma once
#include <ikarus/objects/properties/property.hpp>
#include <ikarus/values/toggle_value.hpp>
#include <ikarus/values/value_type.h>
struct IkarusToggleProperty {
public:
using value_type = IkarusToggleValue;
constexpr auto static PropertyType = IkarusValueType_Toggle;
public:
IkarusToggleProperty(struct IkarusProject * project, int64_t id);
public:
IkarusProperty * property;
};

View file

@ -1,95 +0,0 @@
#pragma once
#include <boost/type_index.hpp>
#include <cppbase/strings.hpp>
#include <ikarus/errors.hpp>
#include <ikarus/objects/properties/property.h>
#include <ikarus/objects/properties/property.hpp>
#include <ikarus/objects/properties/property_scope.hpp>
#include <ikarus/objects/util.hpp>
#include <ikarus/persistence/project.hpp>
#include <ikarus/values/value.hpp>
namespace ikarus::util {
template<typename T>
T * create_property(
struct IkarusProject * project,
char const * name,
struct IkarusPropertyScope * property_scope,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(project, nullptr);
IKARUS_FAIL_IF_NULL(property_scope, nullptr);
IKARUS_FAIL_IF_NAME_INVALID(name, nullptr);
IKARUS_VTRYRV_OR_FAIL(
int64_t const id,
nullptr,
"failed to create property: {}",
IkarusErrorInfo_Database_QueryFailed,
project->db->transact([name, property_scope](auto * db) -> cppbase::Result<int64_t, sqlitecpp::TransactionError> {
TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?)", IkarusObjectType_Property));
auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Property);
TRY(db->execute(
"INSERT INTO `properties`(`id`, `name`, `type`, `source`) VALUES(?, ?, ?, ?)",
id,
name,
T::PropertyType,
property_scope->get_id()
));
return cppbase::ok(id);
})
);
auto * ret = dynamic_cast<T *>(project->get_property(id, T::PropertyType));
IKARUS_FAIL_IF(
ret == nullptr,
nullptr,
fmt::format("created {} cannot be casted down from IkarusProject", boost::typeindex::type_id<T>().pretty_name()),
IkarusErrorInfo_LibIkarus_InvalidState
);
return ret;
}
template<typename T>
typename T::value_type * get_default_value(IkarusProperty const * property, IkarusErrorData * error_out) {
auto * value = ikarus_property_get_default_value(property, error_out);
IKARUS_FAIL_IF_ERROR(nullptr);
auto * ret = boost::variant2::get_if<typename T::value_type *>(&value->data);
IKARUS_FAIL_IF(
ret == nullptr,
nullptr,
fmt::format(
"{} default value is not a(n) {}",
boost::typeindex::type_id<T>().pretty_name(),
boost::typeindex::type_id<typename T::value_type>().pretty_name()
),
IkarusErrorInfo_Database_InvalidState
);
return *ret;
}
template<typename T>
void set_default_value(T * property, typename T::value_type * new_default_value, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(property, );
IKARUS_FAIL_IF_OBJECT_MISSING(property, );
IKARUS_FAIL_IF_NULL(new_default_value, );
auto value_json_str = boost::json::serialize(new_default_value->to_json());
IKARUS_TRYRV_OR_FAIL(
,
"unable to set property default value: {}",
IkarusErrorInfo_Database_QueryFailed,
property->project->db->execute("UPDATE `properties` SET `default_value` = ? WHERE `id` = ?", value_json_str, property->id)
);
}
} // namespace ikarus::util

View file

@ -0,0 +1,15 @@
#include "property.hpp"
#include <ikarus/errors.h>
#include <ikarus/errors.hpp>
#include <ikarus/objects/property.h>
#include <ikarus/persistence/project.hpp>
IkarusProperty::IkarusProperty(
struct IkarusProject * project,
int64_t id,
std::string_view name
):
project{project},
id{id},
name{name} {}

View file

@ -0,0 +1,23 @@
#pragma once
#include <string>
struct IkarusProperty {
consteval static inline auto OBJECT_NAME() -> std::string_view {
return "property";
}
consteval static inline auto TABLE_NAME() -> std::string_view {
return "properties";
}
IkarusProperty(
struct IkarusProject * project,
int64_t id,
std::string_view name
);
struct IkarusProject * project;
int64_t id;
std::string name;
};

View file

@ -1,63 +0,0 @@
#pragma once
#include <string_view>
#include <cppbase/strings.hpp>
#include <ikarus/errors.h>
#include <ikarus/errors.hpp>
#include <ikarus/persistence/project.hpp>
namespace ikarus::util {
template<typename O>
[[nodiscard]] int64_t object_get_id(O const * object, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(object, 0);
IKARUS_FAIL_IF_OBJECT_MISSING(object, 0);
return object->id;
}
template<typename O>
[[nodiscard]] IkarusProject * object_get_project(O const * object, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(object, nullptr);
IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr);
return object->project;
}
template<typename O>
[[nodiscard]] char const * object_get_name(O const * object, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(object, nullptr);
IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr);
IKARUS_VTRYRV_OR_FAIL(
auto const ret,
nullptr,
fmt::runtime(fmt::format("unable to fetch {} name: {{}}", ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)))),
IkarusErrorInfo_Database_QueryFailed,
object->project->db
->template query_one<std::string>(fmt::format("SELECT `name` FROM `{}` WHERE `id` = ?", object->get_table_name()), object->id)
);
return strdup(ret.data());
}
#define IKARUS_FAIL_IF_NAME_INVALID(name, ret) IKARUS_FAIL_IF_NULL(name, ret);
template<typename O>
void object_set_name(O * object, char const * name, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(object, );
IKARUS_FAIL_IF_OBJECT_MISSING(object, );
IKARUS_FAIL_IF_NULL(name, );
IKARUS_FAIL_IF_NAME_INVALID(name, );
IKARUS_TRYRV_OR_FAIL(
,
fmt::runtime(fmt::format("unable to set {} name: {{}}", ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)))),
IkarusErrorInfo_Database_QueryFailed,
object->project->db->execute(fmt::format("UPDATE `{}` SET `name` = ? WHERE `id` = ?", object->get_table_name()), name, object->id)
);
}
} // namespace ikarus::util

View file

@ -1,7 +1,5 @@
#pragma once #pragma once
#include <array>
#include <cppbase/assets.hpp> #include <cppbase/assets.hpp>
#include <cppbase/result.hpp> #include <cppbase/result.hpp>
@ -9,7 +7,10 @@
namespace ikarus { namespace ikarus {
CPPBASE_ASSET(m0_initial_layout, "ikarus/persistence/migrations/m0_initial_layout.sql"); CPPBASE_ASSET(
m0_initial_layout,
"ikarus/persistence/migrations/m0_initial_layout.sql"
);
class Migration : public sqlitecpp::Migration { class Migration : public sqlitecpp::Migration {
public: public:
@ -27,7 +28,11 @@ public:
std::string_view sql; std::string_view sql;
}; };
#define DECLARE_MIGRATION(name) std::make_unique<ikarus::Migration>(static_cast<char const *>(name()), name##_size()) #define DECLARE_MIGRATION(name) \
std::make_unique<ikarus::Migration>( \
static_cast<char const *>(name()), \
name##_size() \
)
constexpr std::string_view DB_VERSION_KEY = "IKARUS_DB_VERSION"; constexpr std::string_view DB_VERSION_KEY = "IKARUS_DB_VERSION";
std::vector<std::unique_ptr<sqlitecpp::Migration>> const MIGRATIONS = []() { std::vector<std::unique_ptr<sqlitecpp::Migration>> const MIGRATIONS = []() {

View file

@ -6,13 +6,14 @@ CREATE TABLE `entities`
CREATE TABLE `entity_values` CREATE TABLE `entity_values`
( (
`id` INTEGER PRIMARY KEY,
`entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE,
`name` TEXT NOT NULL, `name` TEXT NOT NULL,
`type` INT NOT NULL,
`value` TEXT NOT NULL, `value` TEXT NOT NULL,
PRIMARY KEY (`entity`, `name`) PRIMARY KEY (`id`),
) WITHOUT ROWID, STRICT; UNIQUE (`entity`, `name`)
) STRICT;
CREATE TABLE `blueprints` CREATE TABLE `blueprints`
( (
@ -25,14 +26,11 @@ CREATE TABLE `properties`
`id` INTEGER PRIMARY KEY, `id` INTEGER PRIMARY KEY,
`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,
`type` INT NOT NULL, `schema` TEXT NOT NULL,
`cardinality` INT NOT NULL,
`default_value` TEXT NOT NULL, `default_value` TEXT NOT NULL,
`settings` TEXT NOT NULL `settings` TEXT NOT NULL
) STRICT; ) STRICT;
CREATE INDEX `properties_type` ON `properties` (`type`);
CREATE TABLE `entity_blueprint_links` CREATE TABLE `entity_blueprint_links`
( (
`entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE,
@ -43,9 +41,11 @@ CREATE TABLE `entity_blueprint_links`
CREATE TABLE `entity_property_values` CREATE TABLE `entity_property_values`
( (
`id` INTEGER PRIMARY KEY,
`entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE,
`property` INTEGER NOT NULL REFERENCES `properties` (`id`) ON DELETE CASCADE, `property` INTEGER NOT NULL REFERENCES `properties` (`id`) ON DELETE CASCADE,
`value` TEXT NOT NULL, `value` TEXT NOT NULL,
PRIMARY KEY (`entity`, `property`) PRIMARY KEY (`id`),
) WITHOUT ROWID, STRICT; UNIQUE (`entity`, `property`)
) STRICT;

View file

@ -1,339 +1,348 @@
#include "ikarus/persistence/project.h" #include "ikarus/persistence/project.h"
#include <cstdint>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/uuid.hpp>
#include <cppbase/strings.hpp> #include <cppbase/strings.hpp>
#include <ikarus/errors.h>
#include <ikarus/errors.hpp>
#include <ikarus/macros.h>
#include <ikarus/objects/blueprint.hpp> #include <ikarus/objects/blueprint.hpp>
#include <ikarus/objects/entity.hpp> #include <ikarus/objects/entity.hpp>
#include <ikarus/objects/properties/number_property.hpp>
#include <ikarus/objects/properties/property.hpp>
#include <ikarus/objects/properties/text_property.hpp>
#include <ikarus/objects/properties/toggle_property.hpp>
#include <ikarus/objects/util.hpp>
#include <ikarus/persistence/migrations.hpp> #include <ikarus/persistence/migrations.hpp>
#include <ikarus/persistence/project.hpp> #include <ikarus/persistence/project.hpp>
IkarusProject::IkarusProject(std::string_view name, std::string_view path, std::unique_ptr<sqlitecpp::Connection> && db): constexpr char const * DB_PROJECT_ID_KEY = "project_id";
name{name}, constexpr char const * DB_PROJECT_NAME_KEY = "project_name";
path{std::move(path)},
db{std::move(db)},
_blueprints{},
_properties{},
_entities{} {}
IkarusBlueprint * IkarusProject::get_blueprint(int64_t id) { auto create_impl(
return get_cached_object<IkarusBlueprint>(id, this->_blueprints); std::string_view path,
std::string_view name,
IkarusErrorData * error_out
) -> std::unique_ptr<sqlitecpp::Connection> {
IKARUS_VTRYRV_OR_FAIL(
auto db,
nullptr,
"failed to create project db: {}",
IkarusErrorInfo_Database_ConnectionFailed,
sqlitecpp::Connection::open(path)
);
auto close_db = [&]() {
if (db) {
LOG_INFO("closing project db");
auto res = db->close();
if (res.is_error()) {
LOG_ERROR("failed to close project db: {}", res.unwrap_error());
};
}
};
IKARUS_TRYRV_OR_FAIL(
nullptr,
"failed to migrate project db: {}",
IkarusErrorInfo_Database_MigrationFailed,
db->migrate(ikarus::MIGRATIONS).on_error(close_db)
);
IKARUS_TRYRV_OR_FAIL(
nullptr,
"failed to set project id",
IkarusErrorInfo_Database_QueryFailed,
db->execute(
"INSERT INTO `metadata`(`key`, `value`) VALUES(?, ?)",
DB_PROJECT_ID_KEY,
boost::uuids::to_string(boost::uuids::random_generator()())
)
.on_error(close_db)
);
IKARUS_TRYRV_OR_FAIL(
nullptr,
"failed to set project id",
IkarusErrorInfo_Database_QueryFailed,
db->execute(
"INSERT INTO `metadata`(`key`, `value`) VALUES(?, ?)",
DB_PROJECT_NAME_KEY,
name
)
.on_error(close_db)
);
return std::move(db);
} }
auto IkarusProject::uncache(IkarusBlueprint * blueprint) -> void { IkarusProject * ikarus_project_create(
remove_cached_object(blueprint, _blueprints); char const * path,
} char const * name,
IkarusErrorData * error_out
auto IkarusProject::get_entity(int64_t id) -> IkarusEntity * {
return get_cached_object<IkarusEntity>(id, this->_entities);
}
auto IkarusProject::uncache(IkarusEntity * entity) -> void {
remove_cached_object(entity, _entities);
}
auto IkarusProject::get_property(int64_t id, IkarusValueType type) -> IkarusProperty * {
auto const iter = _properties.find(id);
if (iter == _properties.cend()) {
switch (type) {
case IkarusValueType_Toggle:
return _properties.emplace(id, std::make_unique<IkarusToggleProperty>(this, id)).first->second.get();
case IkarusValueType_Number:
return _properties.emplace(id, std::make_unique<IkarusNumberProperty>(this, id)).first->second.get();
case IkarusValueType_Text: return _properties.emplace(id, std::make_unique<IkarusTextProperty>(this, id)).first->second.get();
}
}
return iter->second.get();
}
auto IkarusProject::uncache(IkarusProperty * property) -> void {
remove_cached_object(property, _properties);
}
IkarusProject * ikarus_project_create(char const * path, char const * name, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(path, nullptr);
IKARUS_FAIL_IF_NULL(name, nullptr);
IKARUS_FAIL_IF(cppbase::is_empty_or_blank(path), nullptr, "path must not be empty", IkarusErrorInfo_Client_InvalidInput);
IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), nullptr, "name must not be empty", IkarusErrorInfo_Client_InvalidInput);
boost::filesystem::path fs_path{path};
{
boost::system::error_code ec;
bool const exists = fs::exists(fs_path, ec);
IKARUS_FAIL_IF(
ec && ec != boost::system::errc::no_such_file_or_directory,
nullptr,
fmt::format("unable to check whether path is occupied: {}", ec.message()),
IkarusErrorInfo_Filesystem_AlreadyExists
);
IKARUS_FAIL_IF(exists, nullptr, "path is already occupied", IkarusErrorInfo_Filesystem_AlreadyExists);
}
IKARUS_VTRYRV_OR_FAIL(
auto db,
nullptr,
"failed to create project db: {}",
IkarusErrorInfo_Database_ConnectionFailed,
sqlitecpp::Connection::open(path)
);
IKARUS_TRYRV_OR_FAIL(
nullptr,
"failed to migrate project db: {}",
IkarusErrorInfo_Database_MigrationFailed,
db->migrate(ikarus::MIGRATIONS)
);
if (auto res = db->execute("INSERT INTO `metadata`(`key`, `value`) VALUES(?, ?)", DB_PROJECT_NAME_KEY, name); res.is_error()) {
boost::system::error_code ec;
fs::remove(fs_path, ec);
IKARUS_FAIL_IF(
ec,
nullptr,
"failed to remove project db after being unable to insert name into metadata table: {}",
IkarusErrorInfo_Filesystem_AccessIssue
);
IKARUS_FAIL(nullptr, "failed to insert project name into metadata: {}", IkarusErrorInfo_Database_QueryFailed);
}
return new IkarusProject{name, path, std::move(db)};
}
IkarusProject * ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(name, nullptr);
IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), nullptr, "name must not be empty", IkarusErrorInfo_Client_InvalidInput);
IKARUS_VTRYRV_OR_FAIL(
auto db,
nullptr,
"failed to create project db in memory: {}",
IkarusErrorInfo_Database_ConnectionFailed,
sqlitecpp::Connection::open_in_memory()
);
IKARUS_TRYRV_OR_FAIL(
nullptr,
"failed to migrate project db: {}",
IkarusErrorInfo_Database_MigrationFailed,
db->migrate(ikarus::MIGRATIONS)
);
IKARUS_TRYRV_OR_FAIL(
nullptr,
"failed to insert project name into metadata: {}",
IkarusErrorInfo_Database_QueryFailed,
db->execute("INSERT INTO `metadata`(`key`, `value`) VALUES(?, ?)", DB_PROJECT_NAME_KEY, name)
);
return new IkarusProject{name, ":memory:", std::move(db)};
}
IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(path, nullptr);
IKARUS_FAIL_IF(cppbase::is_empty_or_blank(path), nullptr, "path must not be empty", IkarusErrorInfo_Client_InvalidInput);
IKARUS_VTRYRV_OR_FAIL(
auto db,
nullptr,
"failed to open project db: {}",
IkarusErrorInfo_Database_ConnectionFailed,
sqlitecpp::Connection::open(path)
);
IKARUS_TRYRV_OR_FAIL(
nullptr,
"failed to migrate project db: {}",
IkarusErrorInfo_Database_MigrationFailed,
db->migrate(ikarus::MIGRATIONS)
);
IKARUS_VTRYRV_OR_FAIL(
auto const & name,
nullptr,
"failed to retrieve project name from metadata: {}",
IkarusErrorInfo_Database_QueryFailed,
db->query_one<std::string>("SELECT `value` FROM `metadata` WHERE `key` = ?", DB_PROJECT_NAME_KEY)
);
return new IkarusProject{name, path, std::move(db)};
}
char const * ikarus_project_get_name(IkarusProject const * project, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(project, nullptr);
return project->name.data();
}
void ikarus_project_set_name(IkarusProject * project, char const * new_name, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(project, );
IKARUS_FAIL_IF_NULL(new_name, );
IKARUS_FAIL_IF(cppbase::is_empty_or_blank(new_name), , "name must not be empty", IkarusErrorInfo_Client_InvalidInput);
IKARUS_TRYRV_OR_FAIL(
,
"failed to update project name: {}",
IkarusErrorInfo_Database_QueryFailed,
project->db->execute("UPDATE `metadata` SET `value` = ? WHERE `key` = ?", new_name, DB_PROJECT_NAME_KEY)
);
}
char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(project, nullptr);
return project->path.data();
}
// these take a mutable project right now because the get_cached-* function must be mutable
// since we insert a backreference to the project into the objects, not ideal,
// but fine for now since "mutability" is a vague concept for projects anyway
void ikarus_project_get_blueprints(
IkarusProject * project,
struct IkarusBlueprint ** blueprints_out,
size_t blueprints_out_size,
IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(project, ); IKARUS_FAIL_IF_NULL(path, nullptr);
IKARUS_FAIL_IF_NULL(blueprints_out, ); IKARUS_FAIL_IF_NULL(name, nullptr);
IKARUS_FAIL_IF(
cppbase::is_empty_or_blank(path),
nullptr,
"path must not be empty",
IkarusErrorInfo_Client_InvalidInput
);
IKARUS_FAIL_IF(
cppbase::is_empty_or_blank(name),
nullptr,
"name must not be empty",
IkarusErrorInfo_Client_InvalidInput
);
if (blueprints_out_size == 0) { boost::filesystem::path fs_path{path};
return;
}
int64_t ids[blueprints_out_size]; {
boost::system::error_code ec;
bool const exists = boost::filesystem::exists(fs_path, ec);
IKARUS_FAIL_IF(
ec && ec != boost::system::errc::no_such_file_or_directory,
nullptr,
fmt::format(
"unable to check whether path is occupied: {}",
ec.message()
),
IkarusErrorInfo_Filesystem_AlreadyExists
);
IKARUS_FAIL_IF(
exists,
nullptr,
"path is already occupied",
IkarusErrorInfo_Filesystem_AlreadyExists
);
}
IKARUS_TRYRV_OR_FAIL( if (auto db = create_impl(fs_path.c_str(), name, error_out); !db) {
, LOG_WARN("creating project failed, removing file at {}", path);
"unable to fetch project blueprints from database: {}",
IkarusErrorInfo_Database_QueryFailed,
project->db->query_many_buffered<int64_t>("SELECT `id` FROM `blueprints`", ids, blueprints_out_size)
);
for (size_t i = 0; i < blueprints_out_size; ++i) { boost::system::error_code ec;
blueprints_out[i] = project->get_blueprint(ids[i]); boost::filesystem::remove(fs_path, ec);
}
IKARUS_FAIL_IF(
ec,
nullptr,
"failed to remove project db",
IkarusErrorInfo_Filesystem_MissingPermissions
);
IKARUS_FAIL(
nullptr,
"failed to insert project name into metadata: {}",
IkarusErrorInfo_Database_QueryFailed
);
return nullptr;
} else {
return new IkarusProject{name, path, std::move(db)};
}
} }
size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out) { IkarusProject * ikarus_project_create_in_memory(
IKARUS_FAIL_IF_NULL(project, 0); char const * name,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(name, nullptr);
IKARUS_FAIL_IF(
cppbase::is_empty_or_blank(name),
nullptr,
"name must not be empty",
IkarusErrorInfo_Client_InvalidInput
);
IKARUS_VTRYRV_OR_FAIL( if (auto db = create_impl(":memory:", name, error_out); !db) {
auto const ret, return nullptr;
0, } else {
"unable to fetch project blueprint count from database: {}", IKARUS_TRYRV_OR_FAIL(
IkarusErrorInfo_Database_QueryFailed, nullptr,
project->db->query_one<int64_t>("SELECT COUNT(*) FROM `blueprints`") "failed to migrate project db: {}",
); IkarusErrorInfo_Database_MigrationFailed,
db->migrate(ikarus::MIGRATIONS)
);
return ret; IKARUS_TRYRV_OR_FAIL(
nullptr,
"failed to insert project name into metadata: {}",
IkarusErrorInfo_Database_QueryFailed,
db->execute(
"INSERT INTO `metadata`(`key`, `value`) VALUES(?, ?)",
DB_PROJECT_NAME_KEY,
name
)
);
return new IkarusProject{name, ":memory:", std::move(db)};
}
}
IkarusProject *
ikarus_project_open(char const * path, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(path, nullptr);
IKARUS_FAIL_IF(
cppbase::is_empty_or_blank(path),
nullptr,
"path must not be empty",
IkarusErrorInfo_Client_InvalidInput
);
IKARUS_VTRYRV_OR_FAIL(
auto db,
nullptr,
"failed to open project db: {}",
IkarusErrorInfo_Database_ConnectionFailed,
sqlitecpp::Connection::open(path)
);
IKARUS_TRYRV_OR_FAIL(
nullptr,
"failed to migrate project db: {}",
IkarusErrorInfo_Database_MigrationFailed,
db->migrate(ikarus::MIGRATIONS)
);
IKARUS_VTRYRV_OR_FAIL(
auto const & name,
nullptr,
"failed to retrieve project name from metadata: {}",
IkarusErrorInfo_Database_QueryFailed,
db->query_one<std::string>(
"SELECT `value` FROM `metadata` WHERE `key` = ?",
DB_PROJECT_NAME_KEY
)
);
return new IkarusProject{name, path, std::move(db)};
}
char const * ikarus_project_get_name(
IkarusProject const * project,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(project, nullptr);
return project->name.data();
}
void ikarus_project_set_name(
IkarusProject * project,
char const * new_name,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(project, );
IKARUS_FAIL_IF_NULL(new_name, );
IKARUS_FAIL_IF(
cppbase::is_empty_or_blank(new_name),
,
"name must not be empty",
IkarusErrorInfo_Client_InvalidInput
);
IKARUS_TRYRV_OR_FAIL(
,
"failed to update project name: {}",
IkarusErrorInfo_Database_QueryFailed,
project->db->execute(
"UPDATE `metadata` SET `value` = ? WHERE `key` = ?",
new_name,
DB_PROJECT_NAME_KEY
)
);
}
char const * ikarus_project_get_path(
IkarusProject const * project,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(project, nullptr);
return project->path.c_str();
} }
void ikarus_project_get_entities( void ikarus_project_get_entities(
IkarusProject * project, struct IkarusProject const * project,
struct IkarusEntity ** entities_out, int64_t * ids_out,
size_t entities_out_size, uint64_t ids_out_size,
IkarusErrorData * error_out IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(project, ); IKARUS_FAIL_IF_NULL(project, );
IKARUS_FAIL_IF_NULL(entities_out, ); IKARUS_FAIL_IF_NULL(ids_out, );
if (entities_out_size == 0) { if (ids_out == 0) {
return; return;
} }
int64_t ids[entities_out_size]; IKARUS_TRYRV_OR_FAIL(
,
IKARUS_TRYRV_OR_FAIL( "unable to fetch project entities from database: {}",
, IkarusErrorInfo_Database_QueryFailed,
"unable to fetch project entities from database: {}", project->db->query_many_buffered<int64_t>(
IkarusErrorInfo_Database_QueryFailed, "SELECT `id` FROM `entities`",
project->db->query_many_buffered<int64_t>("SELECT `id` FROM `entities`", ids, entities_out_size) ids_out,
); ids_out_size
)
for (size_t i = 0; i < entities_out_size; ++i) { );
entities_out[i] = project->get_entity(ids[i]);
}
} }
size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out) { size_t ikarus_project_get_entity_count(
IKARUS_FAIL_IF_NULL(project, 0); IkarusProject const * project,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(project, 0);
IKARUS_VTRYRV_OR_FAIL( IKARUS_VTRYRV_OR_FAIL(
auto const ret, auto const ret,
0, 0,
"unable to fetch project entity count from database: {}", "unable to fetch project entity count from database: {}",
IkarusErrorInfo_Database_QueryFailed, IkarusErrorInfo_Database_QueryFailed,
project->db->query_one<int64_t>("SELECT COUNT(*) FROM `entities`") project->db->query_one<int64_t>("SELECT COUNT(`id`) FROM `entities`")
); );
return ret; return ret;
} }
struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) { void ikarus_project_get_blueprints(
IKARUS_FAIL_IF_NULL(project, nullptr); struct IkarusProject const * project,
IKARUS_FAIL_IF_NULL(name, nullptr); int64_t * ids_out,
IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); uint64_t ids_out_size,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(project, );
IKARUS_FAIL_IF_NULL(ids_out, );
// TODO, 'InvalidInput' doesn't really make sense here, we'd need to adjust the macros to support distinguishing between different if (ids_out == 0) {
// errors. In this case `sqlitecpp::MissingRow` and database related errors. Same for the other functions. return;
IKARUS_VTRYRV_OR_FAIL( }
auto const id,
nullptr,
"unable to find entity in database: {}",
IkarusErrorInfo_Client_InvalidInput,
project->db->query_one<int64_t>("SELECT `id` FROM `entities` WHERE `name` = ?", name)
);
return project->get_entity(id); IKARUS_TRYRV_OR_FAIL(
,
"unable to fetch project blueprints from database: {}",
IkarusErrorInfo_Database_QueryFailed,
project->db->query_many_buffered<int64_t>(
"SELECT `id` FROM `blueprints`",
ids_out,
ids_out_size
)
);
} }
struct IkarusProperty * size_t ikarus_project_get_blueprint_count(
get_property_by_name(IkarusProject * project, IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out) { IkarusProject const * project,
IKARUS_FAIL_IF_NULL(project, nullptr); IkarusErrorData * error_out
IKARUS_FAIL_IF_NULL(name, nullptr); ) {
IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); IKARUS_FAIL_IF_NULL(project, 0);
IKARUS_VTRYRV_OR_FAIL( IKARUS_VTRYRV_OR_FAIL(
auto const id_and_type, auto const ret,
nullptr, 0,
"unable to find property in database: {}", "unable to fetch project blueprint count from database: {}",
IkarusErrorInfo_Client_InvalidInput, IkarusErrorInfo_Database_QueryFailed,
project->db->query_one<int64_t, IkarusValueType>( project->db->query_one<int64_t>("SELECT COUNT(`id`) FROM `blueprints`")
"SELECT `id`, `type` FROM `properties` WHERE `name` = ? AND `scope` = ?", );
name,
scope->get_id()
)
);
auto const [id, type] = id_and_type; return ret;
return project->get_property(id, type);
}
IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(project, nullptr);
IKARUS_FAIL_IF_NULL(name, nullptr);
IKARUS_FAIL_IF_NAME_INVALID(name, nullptr);
IKARUS_VTRYRV_OR_FAIL(
auto const id,
nullptr,
"unable to find blueprint in database: {}",
IkarusErrorInfo_Client_InvalidInput,
project->db->query_one<int64_t>("SELECT `id` FROM `blueprints` WHERE `name` = ?", name)
);
return project->get_blueprint(id);
} }

View file

@ -1,58 +1,19 @@
#pragma once #pragma once
#include <cstdint>
#include <memory>
#include <string> #include <string>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <sqlitecpp/connection.hpp> #include <sqlitecpp/connection.hpp>
#include <ikarus/errors.h> #include <ikarus/objects/blueprint.hpp>
#include <ikarus/values/value_type.h> #include <ikarus/objects/entity.hpp>
#include <ikarus/objects/property.hpp>
namespace fs = boost::filesystem;
/// \private
struct IkarusProject { struct IkarusProject {
public: std::string name;
IkarusProject(std::string_view name, std::string_view path, std::unique_ptr<sqlitecpp::Connection> && db); boost::filesystem::path path;
std::unique_ptr<sqlitecpp::Connection> db;
public:
[[nodiscard]] auto get_blueprint(int64_t id) -> struct IkarusBlueprint *;
auto uncache(struct IkarusBlueprint * blueprint) -> void;
[[nodiscard]] auto get_entity(int64_t id) -> struct IkarusEntity *;
auto uncache(struct IkarusEntity * entity) -> void;
// TODO improve this to take a template param so that we don't have to cast in e.g. ikarus_toggle_property_create
[[nodiscard]] auto get_property(int64_t id, IkarusValueType type) -> struct IkarusProperty *;
auto uncache(struct IkarusProperty * property) -> void;
private:
template<typename T>
[[nodiscard]] T * get_cached_object(int64_t id, auto & cache) {
auto const iter = cache.find(id);
if (iter == cache.cend()) {
return cache.emplace(id, std::make_unique<T>(this, id)).first->second.get();
}
return iter->second.get();
}
template<typename T>
void remove_cached_object(T * object, std::unordered_map<int64_t, std::unique_ptr<T>> & cache) {
cache.erase(object->id);
}
public:
std::string name;
std::string_view path;
std::unique_ptr<sqlitecpp::Connection> db;
private:
std::unordered_map<int64_t, std::unique_ptr<struct IkarusBlueprint>> mutable _blueprints;
std::unordered_map<int64_t, std::unique_ptr<struct IkarusProperty>> mutable _properties;
std::unordered_map<int64_t, std::unique_ptr<struct IkarusEntity>> mutable _entities;
}; };
constexpr std::string_view DB_PROJECT_NAME_KEY = "PROJECT_NAME";

217
src/ikarus/values/data.cpp Normal file
View file

@ -0,0 +1,217 @@
#include "data.hpp"
#include <nlohmann/json.hpp>
#include <cppbase/result.hpp>
#include <cppbase/templates.hpp>
#include <ikarus/values/data.h>
#include <ikarus/values/errors.hpp>
#include <ikarus/values/schema.h>
#include <ikarus/values/shared.hpp>
auto get_primitive_type(IkarusValueDataPrimitive const & primitive)
-> IkarusValuePrimitiveType {
return std::visit(
cppbase::overloaded{
[](IkarusValueDataPrimitiveToggle const &) {
return IkarusValuePrimitiveType_Toggle;
},
[](IkarusValueDataPrimitiveNumber const &) {
return IkarusValuePrimitiveType_Number;
},
[](IkarusValueDataPrimitiveString const &) {
return IkarusValuePrimitiveType_Text;
}
},
primitive
);
}
auto IkarusValueData::from_json(nlohmann::json const & json)
-> cppbase::Result<IkarusValueData, IkarusValueDataParseError> {
if (!json.is_object()) {
return cppbase::err(IkarusJsonError{IkarusJsonInvalidTypeError{}});
}
IkarusValueData value{};
VTRY(
auto type,
deserialize_enum<IkarusValueDataType>(
json,
"type",
IkarusValueDataType_Primitive,
IkarusValueDataType_Tuple
)
);
switch (type) {
case IkarusValueDataType_Primitive: {
VTRY(
auto primitive,
deserialize_enum<IkarusValuePrimitiveType>(
json,
"primitive",
IkarusValuePrimitiveType_Toggle,
IkarusValuePrimitiveType_Text
)
);
switch (primitive) {
case IkarusValuePrimitiveType_Toggle: {
VTRY(auto data, deserialize_any<bool>(json, "data"));
value.variant = IkarusValueDataPrimitiveToggle{data};
break;
}
case IkarusValuePrimitiveType_Number: {
VTRY(auto data, deserialize_any<double>(json, "data"));
value.variant = IkarusValueDataPrimitiveNumber{data};
break;
}
case IkarusValuePrimitiveType_Text: {
VTRY(auto data, deserialize_any<std::string>(json, "data"));
value.variant = IkarusValueDataPrimitiveString{data};
break;
}
}
break;
}
case IkarusValueDataType_List: {
std::vector<cppbase::owning_ptr<IkarusValueData>> values_data{};
VTRY(
auto data_json,
deserialize_any<std::vector<nlohmann::json>>(json, "data")
);
values_data.reserve(data_json.size());
for (auto const & data_json : data_json) {
VTRY(auto value_data, IkarusValueData::from_json(data_json));
values_data.emplace_back(
cppbase::make_owning<IkarusValueData>(std::move(value_data))
);
}
value.variant = IkarusValueDataList{values_data};
break;
}
case IkarusValueDataType_Map: {
std::vector<std::pair<
cppbase::owning_ptr<IkarusValueData>,
cppbase::owning_ptr<IkarusValueData>>>
map_data{};
VTRY(
auto map_data_json,
deserialize_any<std::vector<nlohmann::json>>(json, "data")
);
map_data.reserve(map_data_json.size());
for (auto const & pair_json : map_data_json) {
VTRY(auto key_json, get_key(pair_json, "key"));
VTRY(auto value_json, get_key(pair_json, "value"));
VTRY(auto key, IkarusValueData::from_json(*key_json));
VTRY(auto value, IkarusValueData::from_json(*value_json));
map_data.emplace_back(
cppbase::make_owning<IkarusValueData>(key),
cppbase::make_owning<IkarusValueData>(value)
);
}
value.variant = IkarusValueDataMap{map_data};
break;
}
case IkarusValueDataType_Tuple: {
std::vector<cppbase::owning_ptr<IkarusValueData>> values_data{};
VTRY(
auto values_json,
deserialize_any<std::vector<nlohmann::json>>(json, "data")
);
values_data.reserve(values_json.size());
for (auto const & value_json : values_json) {
VTRY(auto value_data, IkarusValueData::from_json(value_json));
values_data.emplace_back(
cppbase::make_owning<IkarusValueData>(value_data)
);
}
value.variant = IkarusValueDataTuple{values_data};
break;
}
}
return cppbase::ok(value);
}
auto IkarusValueData::to_json(
nlohmann::json & json,
IkarusValueData const & value
) -> void {
std::visit(
cppbase::overloaded{
[&](IkarusValueDataPrimitive const & primitive) {
std::visit(
cppbase::overloaded{
[&](IkarusValueDataPrimitiveToggle const & toggle) {
json["type"] = IkarusValueDataType_Primitive;
json["primitive"] = IkarusValuePrimitiveType_Toggle;
json["data"] = toggle.value;
},
[&](IkarusValueDataPrimitiveNumber const & number) {
json["type"] = IkarusValueDataType_Primitive;
json["primitive"] = IkarusValuePrimitiveType_Number;
json["data"] = number.value;
},
[&](IkarusValueDataPrimitiveString const & string) {
json["type"] = IkarusValueDataType_Primitive;
json["primitive"] = IkarusValuePrimitiveType_Text;
json["data"] = string.value;
}
},
primitive
);
},
[&](IkarusValueDataList const & list) {
json["type"] = IkarusValueDataType_List;
json["data"] = list.values |
std::views::transform([](auto const & data) {
nlohmann::json j;
IkarusValueData::to_json(j, *data);
return j;
}) |
std::ranges::to<std::vector<nlohmann::json>>();
},
[&](IkarusValueDataMap const & map) {
json["type"] = IkarusValueDataType_Map;
json["data"] =
map.values | std::views::transform([](auto const & pair) {
nlohmann::json j;
IkarusValueData::to_json(j["key"], *pair.first);
IkarusValueData::to_json(j["value"], *pair.second);
return j;
}) |
std::ranges::to<std::vector<nlohmann::json>>();
},
[&](IkarusValueDataTuple const & tuple) {
json["type"] = IkarusValueDataType_Tuple;
json["data"] = tuple.values |
std::views::transform([](auto const & data) {
nlohmann::json j;
IkarusValueData::to_json(j, *data);
return j;
}) |
std::ranges::to<std::vector<nlohmann::json>>();
}
},
value.variant
);
}

View file

@ -0,0 +1,65 @@
#pragma once
#include <variant>
#include <vector>
#include <nlohmann/json.hpp>
#include <cppbase/pointer.hpp>
#include <cppbase/result.hpp>
#include <ikarus/values/data.h>
#include <ikarus/values/errors.hpp>
#include <ikarus/values/schema.h>
struct IkarusValueData;
struct IkarusValueDataPrimitiveToggle {
bool value;
};
struct IkarusValueDataPrimitiveNumber {
double value;
};
struct IkarusValueDataPrimitiveString {
std::string value;
};
using IkarusValueDataPrimitive = std::variant<
IkarusValueDataPrimitiveToggle,
IkarusValueDataPrimitiveNumber,
IkarusValueDataPrimitiveString>;
struct IkarusValueDataList {
std::vector<cppbase::owning_ptr<IkarusValueData>> values;
};
struct IkarusValueDataMap {
std::vector<std::pair<
cppbase::owning_ptr<IkarusValueData>,
cppbase::owning_ptr<IkarusValueData>>>
values;
};
struct IkarusValueDataTuple {
std::vector<cppbase::owning_ptr<IkarusValueData>> values;
};
struct IkarusValueData {
using IkarusValueDataVariant = std::variant<
IkarusValueDataPrimitive,
IkarusValueDataList,
IkarusValueDataMap,
IkarusValueDataTuple>;
static auto from_json(nlohmann::json const & json)
-> cppbase::Result<IkarusValueData, IkarusValueDataParseError>;
static auto to_json(nlohmann::json & json, IkarusValueData const & value)
-> void;
IkarusValueDataVariant variant;
};
auto get_primitive_type(IkarusValueDataPrimitive const & primitive)
-> IkarusValuePrimitiveType;

View file

@ -1,21 +0,0 @@
#include "entity_property_value.hpp"
#include <ikarus/errors.hpp>
IkarusEntity const * ikarus_entity_property_value_get_entity(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(value, nullptr);
return value->entity;
}
IkarusProperty const * ikarus_entity_property_value_get_property(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(value, nullptr);
return value->property;
}
IkarusValue const * ikarus_entity_property_value_get_value(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(value, nullptr);
return value->value;
}

View file

@ -1 +0,0 @@
#pragma once

View file

@ -0,0 +1,36 @@
#pragma once
#include <variant>
struct IkarusJsonMissingKeyError {};
struct IkarusJsonInvalidTypeError {};
struct IkarusJsonEnumOutOfBoundsError {};
struct IkarusJsonUnknownError {};
using IkarusJsonError = std::variant<
IkarusJsonMissingKeyError,
IkarusJsonInvalidTypeError,
IkarusJsonEnumOutOfBoundsError,
IkarusJsonUnknownError>;
struct IkarusValueSchemaParseError {
IkarusJsonError error;
};
struct IkarusValueDataParseError {
IkarusJsonError error;
};
struct IkarusValueParseErrorDataSchemaMismatch {};
using IkarusValueParseError = std::variant<
IkarusJsonError,
IkarusValueSchemaParseError,
IkarusValueDataParseError,
IkarusValueParseErrorDataSchemaMismatch>;
using IkarusValuesParseError =
std::variant<IkarusJsonMissingKeyError, IkarusValueParseError>;

View file

@ -1,65 +0,0 @@
#include "ikarus/values/number_value.h"
#include <boost/functional/overloaded_function.hpp>
#include <ikarus/values/number_value.hpp>
#include <ikarus/values/value_base.hpp>
IkarusNumberValue::IkarusNumberValue():
IkarusValueData{this} {}
IkarusNumberValue * ikarus_number_value_data_create() {
return new IkarusNumberValue{};
}
double const * ikarus_number_value_data_get(IkarusNumberValue * value, size_t idx) {
return ikarus_value_data_base_get(value, idx);
}
size_t ikarus_number_value_data_get_size(IkarusNumberValue const * value) {
return ikarus_value_data_base_get_size(value);
}
void ikarus_number_value_data_set(IkarusNumberValue * value, size_t idx, double new_data) {
return ikarus_value_data_base_set(value, idx, new_data);
}
void ikarus_number_value_data_remove(IkarusNumberValue * value, size_t idx) {
return ikarus_value_data_base_remove(value, idx);
}
void ikarus_number_value_data_insert(IkarusNumberValue * value, size_t idx, long double new_data) {
return ikarus_value_data_base_insert(value, idx, new_data);
}
void ikarus_number_value_data_clear(IkarusNumberValue * value) {
return ikarus_value_data_base_clear(value);
}
bool ikarus_number_value_data_is_undefined(IkarusNumberValue const * value) {
return ikarus_value_data_base_is_undefined(value);
}
void ikarus_number_value_data_set_undefined(IkarusNumberValue * value, bool undefined) {
return ikarus_value_data_base_set_undefined(value, undefined);
}
char const * ikarus_number_value_data_to_string(IkarusNumberValue const * value) {
return ikarus_value_data_base_to_string(value, [](auto const & value) { return value; });
}
bool ikarus_number_value_data_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs) {
return ikarus_value_data_base_is_equal(lhs, rhs);
}
IkarusNumberValue * ikarus_number_value_data_copy(IkarusNumberValue const * value) {
return ikarus_value_data_base_copy(value);
}
struct IkarusValueData * ikarus_number_value_data_to_value(IkarusNumberValue * value) {
return ikarus_value_data_base_to_value(value);
}
struct IkarusValueData const * ikarus_number_value_data_to_value_data_const(IkarusNumberValue const * value) {
return ikarus_value_data_base_to_value_data_const(value);
}

View file

@ -1,25 +0,0 @@
#pragma once
#include <boost/container/small_vector.hpp>
#include <boost/variant2.hpp>
#include <ikarus/values/value.hpp>
struct IkarusNumberValue : IkarusValueData {
public:
using DataType = double;
public:
explicit IkarusNumberValue();
IkarusNumberValue(IkarusNumberValue const &) = default;
IkarusNumberValue(IkarusNumberValue &&) = default;
IkarusNumberValue & operator=(IkarusNumberValue const &) = default;
IkarusNumberValue & operator=(IkarusNumberValue &&) = default;
~IkarusNumberValue() override = default;
public:
boost::variant2::variant<boost::variant2::monostate, boost::container::small_vector<DataType, SMALL_VEC_VALUE_SIZE>> data{};
};

View file

@ -0,0 +1,177 @@
#include "schema.hpp"
#include <cppbase/templates.hpp>
#include <ikarus/values/data.hpp>
#include <ikarus/values/schema.h>
#include <ikarus/values/shared.hpp>
auto IkarusValueSchema::from_json(nlohmann::json const & json)
-> cppbase::Result<IkarusValueSchema, IkarusValueSchemaParseError> {
if (!json.is_object()) {
return cppbase::err(
IkarusValueSchemaParseError{
IkarusJsonError{IkarusJsonInvalidTypeError{}}
}
);
}
IkarusValueSchema schema{};
VTRY(
auto type,
deserialize_enum<IkarusValueSchemaType>(
json,
"type",
IkarusValueSchemaType_Primitive,
IkarusValueSchemaType_Tuple
)
);
switch (type) {
case IkarusValueSchemaType_Primitive: {
VTRY(
auto primitive,
deserialize_enum<IkarusValuePrimitiveType>(
json,
"primitive",
IkarusValuePrimitiveType_Toggle,
IkarusValuePrimitiveType_Text
)
);
schema.variant = IkarusValueSchemaPrimitive{primitive};
break;
}
case IkarusValueSchemaType_List: {
VTRY(auto sub_schema, IkarusValueSchema::from_json(json["schema"]));
schema.variant = IkarusValueSchemaList{
cppbase::make_owning<IkarusValueSchema>(std::move(sub_schema))
};
break;
}
case IkarusValueSchemaType_Map: {
VTRY(auto key_schema, IkarusValueSchema::from_json(json["key_schema"]));
VTRY(
auto value_schema,
IkarusValueSchema::from_json(json["value_schema"])
);
schema.variant = IkarusValueSchemaMap{
cppbase::make_owning<IkarusValueSchema>(std::move(key_schema)),
cppbase::make_owning<IkarusValueSchema>(std::move(value_schema))
};
break;
}
case IkarusValueSchemaType_Tuple: {
VTRY(
auto sub_schemas_json,
deserialize_any<std::vector<nlohmann::json>>(json, "schemas")
);
std::vector<cppbase::owning_ptr<IkarusValueSchema>> sub_schemas{};
sub_schemas.reserve(sub_schemas_json.size());
for (auto const & sub_schema_json : sub_schemas_json) {
VTRY(auto schema, IkarusValueSchema::from_json(sub_schema_json));
sub_schemas.emplace_back(
cppbase::make_owning<IkarusValueSchema>(std::move(schema))
);
}
schema.variant = IkarusValueSchemaTuple{sub_schemas};
break;
}
}
return cppbase::ok(std::move(schema));
}
auto IkarusValueSchema::to_json(
nlohmann::json & json,
IkarusValueSchema const & schema
) -> void {
std::visit(
cppbase::overloaded{
[&json](IkarusValueSchemaPrimitive const & schema) {
json["type"] = IkarusValueSchemaType_Primitive;
json["primitive"] = schema.type;
},
[&json](IkarusValueSchemaList const & schema) {
json["type"] = IkarusValueSchemaType_List;
IkarusValueSchema::to_json(json["schema"], *schema.sub_schema);
},
[&json](IkarusValueSchemaMap const & schema) {
json["type"] = IkarusValueSchemaType_Map;
IkarusValueSchema::to_json(
json["key_schema"],
*schema.key_schema
);
IkarusValueSchema::to_json(
json["value_schema"],
*schema.value_schema
);
},
[&json](IkarusValueSchemaTuple const & schema) {
json["type"] = IkarusValueSchemaType_Tuple;
std::vector<nlohmann::json> sub_schemas{};
sub_schemas.reserve(schema.sub_schemas.size());
for (auto const & sub_schema : schema.sub_schemas) {
nlohmann::json sub_schema_json{};
IkarusValueSchema::to_json(sub_schema_json, *sub_schema);
sub_schemas.push_back(sub_schema_json);
}
json["schemas"] = sub_schemas;
}
},
schema.variant
);
}
auto IkarusValueSchema::validate(IkarusValueData const & data) const -> bool {
return std::visit(
cppbase::overloaded{
[](IkarusValueSchemaPrimitive const & schema,
IkarusValueDataPrimitive const & data) {
return get_primitive_type(data) == schema.type;
},
[](IkarusValueSchemaList const & schema,
IkarusValueDataList const & data) {
for (auto const & value : data.values) {
if (!schema.sub_schema->validate(*value)) {
return false;
}
}
return true;
},
[](IkarusValueSchemaMap const & schema,
IkarusValueDataMap const & data) {
for (auto const & pair : data.values) {
if (!schema.key_schema->validate(*pair.first) ||
!schema.value_schema->validate(*pair.second)) {
return false;
}
}
return true;
},
[](IkarusValueSchemaTuple const & schema,
IkarusValueDataTuple const & data) {
if (schema.sub_schemas.size() != data.values.size()) {
return false;
}
for (size_t i = 0; i < schema.sub_schemas.size(); ++i) {
if (!schema.sub_schemas[i]->validate(*data.values[i])) {
return false;
}
}
return true;
},
[](auto const &, auto const &) { return false; }
},
variant,
data.variant
);
}

View file

@ -0,0 +1,51 @@
#pragma once
#include <string>
#include <variant>
#include <vector>
#include <nlohmann/json.hpp>
#include <cppbase/pointer.hpp>
#include <cppbase/result.hpp>
#include <ikarus/values/errors.hpp>
#include <ikarus/values/schema.h>
struct IkarusValue;
struct IkarusValueData;
struct IkarusValueSchema;
struct IkarusValueSchemaPrimitive {
IkarusValuePrimitiveType type;
};
struct IkarusValueSchemaList {
cppbase::owning_ptr<IkarusValueSchema> sub_schema;
};
struct IkarusValueSchemaMap {
cppbase::owning_ptr<IkarusValueSchema> key_schema;
cppbase::owning_ptr<IkarusValueSchema> value_schema;
};
struct IkarusValueSchemaTuple {
std::vector<cppbase::owning_ptr<IkarusValueSchema>> sub_schemas;
};
struct IkarusValueSchema {
using IkarusValueSchemaVariant = std::variant<
IkarusValueSchemaPrimitive,
IkarusValueSchemaList,
IkarusValueSchemaMap,
IkarusValueSchemaTuple>;
static auto from_json(nlohmann::json const & json)
-> cppbase::Result<IkarusValueSchema, IkarusValueSchemaParseError>;
static auto to_json(nlohmann::json & json, IkarusValueSchema const & value)
-> void;
auto validate(IkarusValueData const & data) const -> bool;
IkarusValueSchemaVariant variant;
};

View file

@ -0,0 +1,65 @@
#pragma once
#include "errors.hpp"
#include <nlohmann/json.hpp>
#include <cppbase/result.hpp>
#include <cppbase/templates.hpp>
inline auto get_key(nlohmann::json const & json, std::string_view key)
-> cppbase::
Result<nlohmann::json::const_iterator, IkarusJsonMissingKeyError> {
auto iter = json.find(key);
if (iter == json.end()) {
return cppbase::err(IkarusJsonMissingKeyError{});
}
return cppbase::ok(iter);
}
template<cppbase::Enum E>
auto serialize_enum(nlohmann::json & json, std::string_view key, E value)
-> void {
json[key] = static_cast<std::underlying_type_t<E>>(value);
}
template<cppbase::Enum E>
requires std::is_integral_v<std::underlying_type_t<E>>
auto deserialize_enum(
nlohmann::json const & json,
std::string_view key,
E min,
E max
) -> cppbase::Result<E, IkarusJsonError> {
VTRY(auto iter, get_key(json, key));
if (!iter->is_number_integer()) {
return cppbase::err(IkarusJsonError{});
}
auto value = iter->get<std::underlying_type_t<E>>();
if (value < static_cast<std::underlying_type_t<E>>(min) ||
value > static_cast<std::underlying_type_t<E>>(max)) {
return cppbase::err(IkarusJsonError{});
}
return cppbase::ok(static_cast<E>(value));
}
template<typename T>
auto deserialize_any(nlohmann::json const & json, std::string_view key)
-> cppbase::Result<T, IkarusJsonError> {
VTRY(auto iter, get_key(json, key));
try {
return cppbase::ok(iter->get<T>());
} catch (nlohmann::json::type_error const &) {
return cppbase::err(IkarusJsonInvalidTypeError{});
} catch (nlohmann::json::out_of_range const &) {
return cppbase::err(IkarusJsonEnumOutOfBoundsError{});
} catch (nlohmann::json::exception const &) {
return cppbase::err(IkarusJsonUnknownError{});
}
}

View file

@ -1,66 +0,0 @@
#include "ikarus/values/text_value.h"
#include <boost/bind/bind.hpp>
#include <boost/functional/overloaded_function.hpp>
#include <ikarus/values/text_value.hpp>
#include <ikarus/values/value_base.hpp>
IkarusTextValue::IkarusTextValue():
IkarusValueData{this} {}
IkarusTextValue * ikarus_text_value_data_create() {
return new IkarusTextValue{};
}
char const * ikarus_text_value_data_get(IkarusTextValue * value, size_t idx) {
return ikarus_value_data_base_get(value, idx)->data();
}
size_t ikarus_text_value_data_get_size(IkarusTextValue const * value) {
return ikarus_value_data_base_get_size(value);
}
void ikarus_text_value_data_set(IkarusTextValue * value, size_t idx, char const * new_data) {
return ikarus_value_data_base_set(value, idx, new_data);
}
void ikarus_text_value_data_remove(IkarusTextValue * value, size_t idx) {
return ikarus_value_data_base_remove(value, idx);
}
void ikarus_text_value_data_insert(IkarusTextValue * value, size_t idx, char const * new_data) {
return ikarus_value_data_base_insert(value, idx, new_data);
}
void ikarus_text_value_data_clear(IkarusTextValue * value) {
return ikarus_value_data_base_clear(value);
}
bool ikarus_text_value_data_is_undefined(IkarusTextValue const * value) {
return ikarus_value_data_base_is_undefined(value);
}
void ikarus_text_value_data_set_undefined(IkarusTextValue * value, bool undefined) {
return ikarus_value_data_base_set_undefined(value, undefined);
}
char const * ikarus_text_value_data_to_string(IkarusTextValue const * value) {
return ikarus_value_data_base_to_string(value, [](auto const & value) { return value; });
}
bool ikarus_text_value_data_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs) {
return ikarus_value_data_base_is_equal(lhs, rhs);
}
IkarusTextValue * ikarus_text_value_data_copy(IkarusTextValue const * value) {
return ikarus_value_data_base_copy(value);
}
struct IkarusValueData * ikarus_text_value_data_to_value(IkarusTextValue * value) {
return ikarus_value_data_base_to_value(value);
}
struct IkarusValueData const * ikarus_text_value_data_to_value_data_const(IkarusTextValue const * value) {
return ikarus_value_data_base_to_value_data_const(value);
}

View file

@ -1,24 +0,0 @@
#pragma once
#include <boost/container/small_vector.hpp>
#include <ikarus/values/value.hpp>
struct IkarusTextValue : IkarusValueData {
public:
using DataType = std::string;
public:
explicit IkarusTextValue();
IkarusTextValue(IkarusTextValue const &) = default;
IkarusTextValue(IkarusTextValue &&) = default;
IkarusTextValue & operator=(IkarusTextValue const &) = default;
IkarusTextValue & operator=(IkarusTextValue &&) = default;
~IkarusTextValue() override = default;
public:
boost::variant2::variant<boost::variant2::monostate, boost::container::small_vector<DataType, SMALL_VEC_VALUE_SIZE>> data{};
};

View file

@ -1,66 +0,0 @@
#include "ikarus/values/toggle_value.h"
#include <boost/functional/overloaded_function.hpp>
#include <boost/range/adaptors.hpp>
#include <ikarus/values/toggle_value.hpp>
#include <ikarus/values/value_base.hpp>
IkarusToggleValue::IkarusToggleValue():
IkarusValueData{this} {}
IkarusToggleValue * ikarus_toggle_value_data_create() {
return new IkarusToggleValue{};
}
bool const * ikarus_toggle_value_data_get(IkarusToggleValue * value, size_t idx) {
return ikarus_value_data_base_get(value, idx);
}
size_t ikarus_toggle_value_data_get_size(IkarusToggleValue const * value) {
return ikarus_value_data_base_get_size(value);
}
void ikarus_toggle_value_data_set(IkarusToggleValue * value, size_t idx, bool new_data) {
return ikarus_value_data_base_set(value, idx, new_data);
}
void ikarus_toggle_value_data_remove(IkarusToggleValue * value, size_t idx) {
return ikarus_value_data_base_remove(value, idx);
}
void ikarus_toggle_value_data_insert(IkarusToggleValue * value, size_t idx, bool new_data) {
return ikarus_value_data_base_insert(value, idx, new_data);
}
void ikarus_toggle_value_data_clear(IkarusToggleValue * value) {
return ikarus_value_data_base_clear(value);
}
bool ikarus_toggle_value_data_is_undefined(IkarusToggleValue const * value) {
return ikarus_value_data_base_is_undefined(value);
}
void ikarus_toggle_value_data_set_undefined(IkarusToggleValue * value, bool undefined) {
return ikarus_value_data_base_set_undefined(value, undefined);
}
char const * ikarus_toggle_value_data_to_string(IkarusToggleValue const * value) {
return ikarus_value_data_base_to_string(value, [](auto const & value) { return value ? "" : ""; });
}
bool ikarus_toggle_value_data_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs) {
return ikarus_value_data_base_is_equal(lhs, rhs);
}
IkarusToggleValue * ikarus_toggle_value_data_copy(IkarusToggleValue const * value) {
return ikarus_value_data_base_copy(value);
}
struct IkarusValueData * ikarus_toggle_value_data_to_value(IkarusToggleValue * value) {
return ikarus_value_data_base_to_value(value);
}
struct IkarusValueData const * ikarus_toggle_value_data_to_value_data_const(IkarusToggleValue const * value) {
return ikarus_value_data_base_to_value_data_const(value);
}

View file

@ -1,24 +0,0 @@
#pragma once
#include <boost/container/small_vector.hpp>
#include <ikarus/values/value.hpp>
struct IkarusToggleValue : IkarusValueData {
public:
using DataType = bool;
public:
explicit IkarusToggleValue();
IkarusToggleValue(IkarusToggleValue const &) = default;
IkarusToggleValue(IkarusToggleValue &&) = default;
IkarusToggleValue & operator=(IkarusToggleValue const &) = default;
IkarusToggleValue & operator=(IkarusToggleValue &&) = default;
~IkarusToggleValue() override = default;
public:
boost::variant2::variant<boost::variant2::monostate, boost::container::small_vector<DataType, SMALL_VEC_VALUE_SIZE>> data{};
};

View file

@ -1,134 +1,58 @@
#include "ikarus/values/value.h" #include "value.hpp"
#include <string> #include <ikarus/values/data.h>
#include <ikarus/values/data.hpp>
#include <ikarus/values/schema.h>
#include <ikarus/values/schema.hpp>
#include <ikarus/values/shared.hpp>
#include <ikarus/values/value.h>
#include <boost/container/small_vector.hpp> auto IkarusValue::from_json(nlohmann::json const & json)
#include <boost/json/src.hpp> -> cppbase::Result<IkarusValue, IkarusValueParseError> {
if (!json.is_object()) {
return cppbase::err(IkarusJsonError{IkarusJsonInvalidTypeError{}});
}
// required for header-only inclusion IkarusValue value{};
#include <cppbase/templates.hpp>
#include <ikarus/values/number_value.hpp> VTRY(value.schema, IkarusValueSchema::from_json(json["schema"]));
#include <ikarus/values/text_value.hpp> VTRY(value.data, IkarusValueData::from_json(json["data"]));
#include <ikarus/values/toggle_value.hpp>
#include <ikarus/values/value.hpp>
#include <ikarus/values/value_cardinality.h>
#include <ikarus/values/value_type.h>
IkarusValue::IkarusValue(Data data, IkarusValueCardinality cardinality): if (!value.schema.validate(value.data)) {
data{data}, return cppbase::err(IkarusValueParseErrorDataSchemaMismatch{});
cardinality{cardinality} {} }
cppbase::Result<IkarusValue *, IkarusValue::FromJsonError> IkarusValue::from_json(boost::json::value json) { return cppbase::ok(std::move(value));
if (auto const * obj = json.if_object(); obj == nullptr) {
return cppbase::err(FromJsonError{});
} else {
int64_t const * type;
int64_t const * cardinality;
boost::json::value const * data;
if (auto const * type_value = obj->if_contains(IKARUS_VALUE_JSON_TYPE_KEY); type_value == nullptr) {
return cppbase::err(FromJsonError{});
} else if (type = type_value->if_int64(); type == nullptr) {
return cppbase::err(FromJsonError{});
}
if (auto const * cardinality_value = obj->if_contains(IKARUS_VALUE_JSON_CARDINALITY_KEY); cardinality_value == nullptr) {
return cppbase::err(FromJsonError{});
} else if (cardinality = cardinality_value->if_int64(); cardinality == nullptr) {
return cppbase::err(FromJsonError{});
} else if (*cardinality != IkarusValueCardinality_Single && *cardinality != IkarusValueCardinality_Multiple) {
return cppbase::err(FromJsonError{});
}
if (data = obj->if_contains(IKARUS_VALUE_JSON_DATA_KEY); data == nullptr) {
return cppbase::err(FromJsonError{});
}
auto create_value = [data, cardinality]<typename T>() -> cppbase::Result<IkarusValue *, FromJsonError> {
T * ret = nullptr;
ret->cardinality = *cardinality;
if (data->is_null()) {
ret = new T{};
ret->data = boost::variant2::monostate{};
} else {
auto res =
boost::json::try_value_to<boost::container::small_vector<typename T::DataType, IkarusValue::SMALL_VEC_VALUE_SIZE>>(*data
);
if (res.has_error()) {
return cppbase::err(FromJsonError{});
}
ret = new T{};
ret->data = std::move(res.value());
}
return cppbase::ok(ret);
};
switch (*type) {
case IkarusValueType_Toggle: {
return create_value.operator()<IkarusToggleValue>();
}
case IkarusValueType_Number: {
return create_value.operator()<IkarusNumberValue>();
}
case IkarusValueType_Text: {
return create_value.operator()<IkarusTextValue>();
}
default: {
return cppbase::err(FromJsonError{});
}
}
}
} }
boost::json::value IkarusValue::to_json() const { auto IkarusValue::to_json(nlohmann::json & json, IkarusValue const & value)
auto type = std::visit( -> void {
cppbase::overloaded{ IkarusValueSchema::to_json(json["schema"], value.schema);
[]([[maybe_unused]] IkarusToggleValue const * value) { return IkarusValueType_Toggle; }, IkarusValueData::to_json(json["data"], value.data);
[]([[maybe_unused]] IkarusNumberValue const * value) { return IkarusValueType_Number; },
[]([[maybe_unused]] IkarusTextValue const * value) { return IkarusValueType_Text; }
},
data
);
auto data_json = std::visit(
[]<typename T>(T const * value) -> boost::json::value {
return std::visit(
cppbase::overloaded{
[]([[maybe_unused]] boost::variant2::monostate const & data) -> boost::json::value { return nullptr; },
[](auto const & data) -> boost::json::value { return boost::json::value_from(data); }
},
value->data
);
},
data
);
return {
{ IKARUS_VALUE_JSON_TYPE_KEY, type},
{IKARUS_VALUE_JSON_CARDINALITY_KEY, cardinality},
{ IKARUS_VALUE_JSON_DATA_KEY, data_json}
};
} }
void ikarus_value_visit( auto IkarusValues::from_json(nlohmann::json const & json)
IkarusValue * value, -> cppbase::Result<IkarusValues, IkarusValuesParseError> {
void (*toggle_visitor)(IkarusToggleValue *, void *), IkarusValues values{};
void (*number_visitor)(IkarusNumberValue *, void *),
void (*text_visitor)(IkarusTextValue *, void *), for (auto const & json_entry : json) {
void * data VTRY(auto name_json, get_key(json_entry, "name"));
) {
std::visit( if (!name_json->is_string()) {
cppbase::overloaded{ return cppbase::err(IkarusJsonInvalidTypeError{});
[toggle_visitor, data](IkarusToggleValue * toggle_value) { toggle_visitor(toggle_value, data); }, }
[number_visitor, data](IkarusNumberValue * number_value) { number_visitor(number_value, data); },
[text_visitor, data](IkarusTextValue * text_value) { text_visitor(text_value, data); } VTRY(auto value_json, get_key(json_entry, "value"));
},
value->data VTRY(auto value, IkarusValue::from_json(*value_json));
);
values.values.emplace_back(
std::make_pair(
std::move(name_json->get<std::string_view>()),
std::move(value)
)
);
}
return cppbase::ok(std::move(values));
} }

View file

@ -1,98 +1,28 @@
#pragma once #pragma once
#include <boost/json.hpp> #include <nlohmann/json.hpp>
#include <cppbase/result.hpp> #include <cppbase/result.hpp>
#include <ikarus/errors.hpp> #include <ikarus/values/data.hpp>
#include <ikarus/persistence/project.hpp> #include <ikarus/values/errors.hpp>
#include <ikarus/values/value_cardinality.h> #include <ikarus/values/schema.hpp>
constexpr auto IKARUS_VALUE_JSON_TYPE_KEY = "type";
constexpr auto IKARUS_VALUE_JSON_CARDINALITY_KEY = "card";
constexpr auto IKARUS_VALUE_JSON_DATA_KEY = "data";
struct IkarusValue { struct IkarusValue {
public: IkarusValueSchema schema;
using Data = std::variant<struct IkarusToggleValue *, struct IkarusNumberValue *, struct IkarusTextValue *>; IkarusValueData data;
constexpr static auto SMALL_VEC_VALUE_SIZE = 8;
public: static auto from_json(nlohmann::json const & json)
explicit IkarusValue(Data data, IkarusValueCardinality cardinality); -> cppbase::Result<IkarusValue, IkarusValueParseError>;
static auto to_json(nlohmann::json & json, IkarusValue const & value)
IkarusValue(IkarusValue const &) = default; -> void;
IkarusValue(IkarusValue &&) noexcept = default;
IkarusValue & operator=(IkarusValue const &) = default;
IkarusValue & operator=(IkarusValue &&) noexcept = default;
virtual ~IkarusValue() = default;
public:
struct FromJsonError {};
[[nodiscard]] static cppbase::Result<IkarusValue *, FromJsonError> from_json(boost::json::value json);
[[nodiscard]] boost::json::value to_json() const;
public:
Data data;
IkarusValueCardinality cardinality;
}; };
template<> struct IkarusValues {
struct fmt::formatter<IkarusValue::FromJsonError> { static auto from_json(nlohmann::json const & json)
template<typename FormatParseContext> -> cppbase::Result<IkarusValues, IkarusValuesParseError>;
constexpr static auto parse(FormatParseContext & ctx) { static auto to_json(nlohmann::json & json, IkarusValues const & values)
return ctx.end(); -> void;
}
constexpr static auto format([[maybe_unused]] IkarusValue::FromJsonError const & error, fmt::format_context & ctx) { std::vector<std::pair<std::string, IkarusValue>> values;
return fmt::format_to(ctx.out(), "unable to parse ikarus value JSON");
}
}; };
template<typename... Args>
IkarusValue * fetch_value_from_db(IkarusProject * project, IkarusErrorData * error_out, std::string_view query, Args &&... args) {
IKARUS_VTRYRV_OR_FAIL(
auto const value_str,
nullptr,
"unable to fetch entity property value from database: {}",
IkarusErrorInfo_Database_QueryFailed,
project->db->query_one<std::string>(query, std::forward<Args>(args)...)
);
boost::json::error_code ec{};
boost::json::value json_value = boost::json::parse(value_str, ec);
if (ec) {
IKARUS_SET_ERROR(fmt::format("invalid json is stored in database: {}", ec.message()), IkarusErrorInfo_Database_InvalidState);
return nullptr;
}
IKARUS_VTRYRV_OR_FAIL(
auto const ret,
nullptr,
"unable to fetch entity property value: {}",
IkarusErrorInfo_LibIkarus_CannotPerformOperation,
IkarusValue::from_json(std::move(json_value))
);
return ret;
}
#define IKARUS_FAIL_IF_VALUE_MISSING_IMPL(var_name, entity, name, ret) \
IKARUS_VTRYRV_OR_FAIL( \
bool const var_name, \
ret, \
"unable to check whether value exists: {}", \
IkarusErrorInfo_Database_QueryFailed, \
(entity)->project->db->template query_one<bool>( \
"SELECT EXISTS(SELECT 1 FROM `entity_values` WHERE `entity` = ? AND `name` = ?)", \
(entity)->id, \
name \
) \
) \
\
IKARUS_FAIL_IF(!(var_name), ret, "entity value does not exist", IkarusErrorInfo_Client_Misuse);
#define IKARUS_FAIL_IF_VALUE_MISSING(entity, name, ret) IKARUS_FAIL_IF_VALUE_MISSING_IMPL(CPPBASE_UNIQUE_NAME(exists), entity, name, ret);

View file

@ -1,126 +0,0 @@
#pragma once
#include <ranges>
#include <boost/container/small_vector.hpp>
#include <cppbase/templates.hpp>
template<typename V>
typename V::DataType const * ikarus_value_data_base_get(V * value, size_t idx) {
if (auto * data = boost::variant2::get_if<boost::container::small_vector<typename V::DataType, IkarusValueData::SMALL_VEC_VALUE_SIZE>>(
&value->data
);
data != nullptr) {
return &(*data)[idx];
}
return nullptr;
}
template<typename V>
size_t ikarus_value_data_base_get_size(V const * value) {
if (auto * data = boost::variant2::get_if<boost::container::small_vector<typename V::DataType, IkarusValueData::SMALL_VEC_VALUE_SIZE>>(
&value->data
);
data != nullptr) {
return data->size();
}
return 0;
}
template<typename V>
void ikarus_value_data_base_set(V * value, size_t idx, typename V::DataType new_data) {
if (auto * data = boost::variant2::get_if<boost::container::small_vector<typename V::DataType, IkarusValueData::SMALL_VEC_VALUE_SIZE>>(
&value->data
);
data != nullptr) {
(*data)[idx] = new_data;
}
}
template<typename V>
void ikarus_value_data_base_remove(V * value, size_t idx) {
if (auto * data = boost::variant2::get_if<boost::container::small_vector<typename V::DataType, IkarusValueData::SMALL_VEC_VALUE_SIZE>>(
&value->data
);
data != nullptr) {
data->erase(data->begin() + idx);
}
}
template<typename V>
void ikarus_value_data_base_insert(V * value, size_t idx, typename V::DataType new_data) {
if (auto * data = boost::variant2::get_if<boost::container::small_vector<typename V::DataType, IkarusValueData::SMALL_VEC_VALUE_SIZE>>(
&value->data
);
data != nullptr) {
data->insert(data->begin() + idx, new_data);
}
}
template<typename V>
void ikarus_value_data_base_clear(V * value) {
if (auto * data = boost::variant2::get_if<boost::container::small_vector<typename V::DataType, IkarusValueData::SMALL_VEC_VALUE_SIZE>>(
&value->data
);
data != nullptr) {
data->clear();
}
}
template<typename V>
bool ikarus_value_data_base_is_undefined(V const * value) {
return boost::variant2::holds_alternative<boost::variant2::monostate>(value->data);
}
template<typename V>
void ikarus_value_data_base_set_undefined(V * value, bool undefined) {
if (undefined) {
value->data = boost::variant2::monostate{};
} else {
value->data = boost::container::small_vector<typename V::DataType, IkarusValueData::SMALL_VEC_VALUE_SIZE>{};
}
}
template<typename V, std::invocable<typename V::DataType> F>
char const * ikarus_value_data_base_to_string(V const * value, F transformer) {
return boost::variant2::visit(
cppbase::overloaded{
[](boost::variant2::monostate const &) -> char const * { return nullptr; },
[&transformer](auto const & data) -> char const * {
auto buffer = fmt::memory_buffer{};
fmt::format_to(
std::back_inserter(buffer),
"{}",
fmt::join(data | std::views::transform(std::forward<F>(transformer)), ", ")
);
return buffer.data();
}
},
value->data
);
}
template<typename V>
bool ikarus_value_data_base_is_equal(V const * lhs, V const * rhs) {
return lhs->data == rhs->data;
}
template<typename V>
V * ikarus_value_data_base_copy(V const * value) {
return new V{*value};
}
template<typename V>
struct IkarusValueData * ikarus_value_data_base_to_value(V * value) {
return static_cast<IkarusValueData *>(value);
}
template<typename V>
struct IkarusValueData const * ikarus_value_data_base_to_value_data_const(V const * value) {
return static_cast<IkarusValueData const *>(value);
}

0
tests/CMakeLists.txt Normal file
View file

View file

@ -0,0 +1,5 @@
set(CMAKE_SYSTEM_NAME Darwin)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_PREFIX_PATH "$ENV{HOMEBREW_PREFIX}/opt/llvm")
set(ICU_ROOT "$ENV{HOMEBREW_PREFIX}/opt/icu4c")

View file

@ -1,3 +1,8 @@
# order is important here since sqlitecpp otherwise duplicates cppbase # order is important here since sqlitecpp otherwise duplicates cppbase
add_subdirectory(cppbase) add_subdirectory(cppbase)
add_subdirectory(sqlitecpp) add_subdirectory(sqlitecpp)
add_subdirectory(json)
if (LIBIKARUS_ENABLE_TESTS)
add_subdirectory(catch2)
endif ()

1
vendor/catch2 vendored Submodule

@ -0,0 +1 @@
Subproject commit f5cee49c71e7c0e805695b03e8af533783789762

1
vendor/json vendored Submodule

@ -0,0 +1 @@
Subproject commit 6057b31df7fe0b958d1795defc108d1f26e26441

2
vendor/sqlitecpp vendored

@ -1 +1 @@
Subproject commit 32fa3c1a9beae1a2c49fdd08cc659ea6153da666 Subproject commit 8edc04b73bdee4b487ca59abd68615fa66c7948b