From ca4d4a5dbe0f9ee3d89b595d1a6b44c6986463a5 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 22 Aug 2023 00:27:22 +0200 Subject: [PATCH 001/166] a new beginning Signed-off-by: Folling --- .clang-format | 222 ++++++++++++++++++ .gitignore | 58 +++++ .gitmodules | 9 + CMakeLists.txt | 55 +++++ README.md | 11 + docs/DoxyFile | 16 ++ docs/enum_format_fix.js | 11 + docs/header.html | 97 ++++++++ implementation_details | 12 + include/CMakeLists.txt | 8 + include/ikarus/macros.h | 27 +++ include/ikarus/stdtypes.h | 8 + include/ikarus/types/id.h | 58 +++++ include/ikarus/types/object.h | 164 +++++++++++++ include/ikarus/types/object_scope.h | 149 ++++++++++++ include/ikarus/types/object_type.h | 63 +++++ include/ikarus/types/property_type.h | 31 +++ include/ikarus/types/value.h | 161 +++++++++++++ src/CMakeLists.txt | 7 + src/types/object_scope.cpp | 209 +++++++++++++++++ src/types/value.cpp | 337 +++++++++++++++++++++++++++ vendor/CMakeLists.txt | 2 + vendor/catch2 | 1 + vendor/doxygen-awesome-css | 1 + vendor/sqlitecpp | 1 + 25 files changed, 1718 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 docs/DoxyFile create mode 100644 docs/enum_format_fix.js create mode 100644 docs/header.html create mode 100644 implementation_details create mode 100644 include/CMakeLists.txt create mode 100644 include/ikarus/macros.h create mode 100644 include/ikarus/stdtypes.h create mode 100644 include/ikarus/types/id.h create mode 100644 include/ikarus/types/object.h create mode 100644 include/ikarus/types/object_scope.h create mode 100644 include/ikarus/types/object_type.h create mode 100644 include/ikarus/types/property_type.h create mode 100644 include/ikarus/types/value.h create mode 100644 src/CMakeLists.txt create mode 100644 src/types/object_scope.cpp create mode 100644 src/types/value.cpp create mode 100644 vendor/CMakeLists.txt create mode 160000 vendor/catch2 create mode 160000 vendor/doxygen-awesome-css create mode 160000 vendor/sqlitecpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..0372fa7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,222 @@ +BasedOnStyle: Google + +AccessModifierOffset: -4 + +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: + Enabled: false +AlignConsecutiveBitFields: + Enabled: false +AlignConsecutiveDeclarations: + Enabled: false +AlignConsecutiveMacros: AcrossEmptyLines +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true + +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true + +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes + +BinPackArguments: false +BinPackParameters: false + +BitFieldColonSpacing: Both + +BraceWrapping: + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: MultiLine + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyNamespace: false + SplitEmptyRecord: false + +BreakAfterAttributes: Never +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeConceptDeclarations: Always +# BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: false + +ColumnLimit: 128 + +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 + +Cpp11BracedListStyle: true + +DerivePointerAlignment: false + +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: Always + +FixNamespaceComments: false + +IncludeBlocks: Regroup +IncludeCategories: + # Relative Includes + # "blubb.h" / "blubb/blubber.h" + - Regex: '^".+\.(h|hpp)"$' + Priority: 1 + + # C Includes + # + - Regex: '^<[a-z0-9_]+\.h>$' + Priority: 2 + + # C++ Includes + # + - Regex: '^<[a-z0-9_]+>$' + Priority: 3 + + # expected + # + - Regex: '^$' + Priority: 4 + + # libfmt + # + - Regex: '^$' + Priority: 5 + + # nlohmann::json + # + - Regex: '^$' + Priority: 6 + + # ranges + # + - Regex: '^$' + Priority: 7 + + # ICU + # + - Regex: '^$' + Priority: 8 + + # ranges + # + - Regex: '^$' + Priority: 9 + + # ranges + # + - Regex: '^$' + Priority: 10 + + # ranges + # + - Regex: '^$' + Priority: 11 + + # ranges + # + - Regex: '^$' + Priority: 12 + + # ranges + # ranges + # + - Regex: '^$' + Priority: 13 + +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: NoIndent +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: true +InsertNewlineAtEOF: true + +# InsertNewlineAtEOF: true +# IntegerLiteralSeparator: +# Binary: 0 +# Decimal: 3 +# Hex: -1 + +KeepEmptyLinesAtTheStartOfBlocks: false + +LambdaBodyIndentation: Signature +Language: Cpp + +# LineEnding: LF + +MaxEmptyLinesToKeep: 1 + +NamespaceIndentation: None + +PackConstructorInitializers: Never + +PointerAlignment: Middle +QualifierAlignment: Right +# QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ] +ReferenceAlignment: Left + +ReflowComments: true +# RemoveSemicolon: true + +RequiresClausePosition: OwnLine +# RequiresExpressionIndentation: OuterScope + +SeparateDefinitionBlocks: Always + +SortIncludes: CaseInsensitive +SortUsingDeclarations: true + +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false + +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: 1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++20 + +TabWidth: 4 +UseTab: Never diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7ab8cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# C++ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +build +docs/generated +gen/script_old/target +include/ikarus/entities +include/ikarus/project.h +src/generated/**/*.cpp +src/generated/**/*.hpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a26cc62 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "vendor/catch2"] + path = vendor/catch2 + url = git@github.com:catchorg/Catch2.git +[submodule "vendor/sqlitecpp"] + path = vendor/sqlitecpp + url = ssh://git@git.rewritesarebliss.com:16658/Folling/sqlitecpp.git +[submodule "vendor/doxygen-awesome-css"] + path = vendor/doxygen-awesome-css + url = git@github.com:jothepro/doxygen-awesome-css.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a7faf39 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.18) +project(ikarus) + +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_STANDARD 20) + +set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) + +add_subdirectory(vendor) +add_subdirectory(include) +add_subdirectory(src) + +add_library( + libikarus OBJECT + ${INCLUDE_FILES} + ${SOURCE_FILES} +) + +target_include_directories( + libikarus PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include +) + +target_include_directories( + libikarus PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/src +) + +target_link_libraries( + libikarus PUBLIC + Catch2::Catch2WithMain +) + +target_link_libraries( + libikarus PRIVATE + cppbase + sqlitecpp +) + +set_target_properties( + libikarus PROPERTIES + LINKER_LANGUAGE CXX +) + +add_executable(ikarus_tests ${SOURCE_FILES}) +target_link_libraries(ikarus_tests PRIVATE Catch2::Catch2WithMain) + +target_include_directories( + ikarus_tests PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include +) + +include(CTest) +include(vendor/catch2/extras/Catch.cmake) +catch_discover_tests(ikarus_tests) diff --git a/README.md b/README.md new file mode 100644 index 0000000..57b6325 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +### Data Longevity + +All data returned by libikarus is ephemeral and only represents the state of the project at the time of the request. +A snapshot if you will. +One must not rely on it representing the actual state of the project at any given time. The data is simply copied +from the underlying data sources and returned to the caller. + +No mechanisms are provided to avoid race conditions. LibIkarus itself should only be used in a single-threaded context. +However, nothing breaks if you do use it in a multithreaded context, that is, libikarus is threadsafe. +You just cannot rely on the data being consistent. +This goes especially for inter-process access to the same project. \ No newline at end of file diff --git a/docs/DoxyFile b/docs/DoxyFile new file mode 100644 index 0000000..61f3460 --- /dev/null +++ b/docs/DoxyFile @@ -0,0 +1,16 @@ +DISABLE_INDEX = NO +EXCLUDE = ../vendor ../build +EXCLUDE_PATTERNS = cmake-* +FILE_PATTERNS = *.h *.hpp *.tpp *.ipp *.cpp +FULL_SIDEBAR = NO +GENERATE_LATEX = NO +GENERATE_TREEVIEW = YES +HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5 +HTML_EXTRA_FILES = ../vendor/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js ../vendor/doxygen-awesome-css/doxygen-awesome-fragment-copy-button.js ./enum_format_fix.js +HTML_EXTRA_STYLESHEET = ../vendor/doxygen-awesome-css/doxygen-awesome.css +HTML_HEADER = header.html +INPUT = .. +OUTPUT_DIRECTORY = generated +PROJECT_BRIEF = A C-API implementation for Ikarus, a tool for worldbuilding. +PROJECT_NAME = LIBIKARUS +RECURSIVE = YES \ No newline at end of file diff --git a/docs/enum_format_fix.js b/docs/enum_format_fix.js new file mode 100644 index 0000000..2507ab8 --- /dev/null +++ b/docs/enum_format_fix.js @@ -0,0 +1,11 @@ +// maximum efficiency +function enumFormatFix() { + Array.from(document.getElementsByClassName("memItemRight")).forEach((elem) => { + elem.innerHTML = elem.innerHTML.replaceAll("
", ""); + elem.innerHTML = elem.innerHTML.replaceAll(" ", ""); + elem.innerHTML = elem.innerHTML.replaceAll("{", "{
    "); + elem.innerHTML = elem.innerHTML.replaceAll("\n,", ","); + elem.innerHTML = elem.innerHTML.replaceAll(",", ",
    "); + elem.innerHTML = elem.innerHTML.replaceAll("}", "
}"); + }); +} \ No newline at end of file diff --git a/docs/header.html b/docs/header.html new file mode 100644 index 0000000..dfce9a1 --- /dev/null +++ b/docs/header.html @@ -0,0 +1,97 @@ + + + + + + + + + $projectname: $title + $title + + + + + + + + + + + + + + + + + $treeview + $search + $mathjax + $darkmode + + $extrastylesheet + + + + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
$projectname $projectnumber + +
+ +
$projectbrief
+
+
$projectbrief
+
$searchbox
$searchbox
+
+ + diff --git a/implementation_details b/implementation_details new file mode 100644 index 0000000..42b38fc --- /dev/null +++ b/implementation_details @@ -0,0 +1,12 @@ +This list is intended to help keep the documentation up to date. +If you make changes to, for example, templates, always check the documentation for templates. +But sometimes information is shared. and referenced in multiple places. This helps keep track of that. + +Usage: Search for these keys prefixed with IMPLEMENTATION_DETAIL_* to change documentation in relevant places. + +DATABASE: References to the usage of a database +TREE_LAYOUT: References to our usage of a tree layout +OBJECT_TYPES: References to the types of objects +OBJECT_SCOPES: References to the usage of object scopes +PROPERTY_TYPES: The property types that currently exist +LAZY_VALUE_CREATION: The fact that values are created lazily \ No newline at end of file diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000..2e91ee9 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,8 @@ +file( + GLOB_RECURSE + FILES + "*.h" +) + +set(INCLUDE_FILES ${FILES} PARENT_SCOPE) + diff --git a/include/ikarus/macros.h b/include/ikarus/macros.h new file mode 100644 index 0000000..fdc6510 --- /dev/null +++ b/include/ikarus/macros.h @@ -0,0 +1,27 @@ +#pragma once + +#if defined(__unix__) + +#define IKA_OS_FAMILY_UNIX +#define IKA_API __attribute__((visibility("default"))) + +#if defined(linux) +#define IKA_OS_LINUX +#endif + +#elif defined(_WIN32) || defined(WIN32) +#define IKA_OS_WIN +#define IKA_API __declspec(dllexport) +#endif + +#ifndef IKA_API +#define IKA_API +#endif + +#ifdef __cplusplus +#define IKARUS_BEGIN_HEADER extern "C" { +#define IKARUS_END_HEADER } +#else +#define IKARUS_BEGIN_HEADER +#define IKARUS_END_HEADER +#endif diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h new file mode 100644 index 0000000..2f467cf --- /dev/null +++ b/include/ikarus/stdtypes.h @@ -0,0 +1,8 @@ +#pragma once + +#ifdef __cplusplus +#include +using std::size_t; +#else +#include +#endif diff --git a/include/ikarus/types/id.h b/include/ikarus/types/id.h new file mode 100644 index 0000000..a205e43 --- /dev/null +++ b/include/ikarus/types/id.h @@ -0,0 +1,58 @@ +// IMPLEMENTATION_DETAIL_DATABASE + +/// \file id.h +/// \author Folling + +#pragma once + +#include + +IKARUS_BEGIN_HEADER + +#include +#include + +/// \defgroup id Ids +/// \brief Ids are used to identify objects in the database. +/// \details They are stored as 64 bit integers with the following layout: +/// - first 8 bits: #IkarusObjectType - 255 possible values, 0 for special values +/// - last 56 bits: incremented counter generated by the database +/// @{ + +/// \brief A wrapper around a 64 bit integer that represents the id of an object. +/// \details They are stored as 64 bit integers with the following layout: +/// - first 8 bits: #IkarusObjectType - 255 possible values, 0 for special values +/// - last 56 bits: incremented counter generated by the database +struct IkarusId { + /// \private \brief The value of the id. + uint64_t value; +}; + +/// \brief A special id returned by failed functions. +IkarusId const IKARUS_ID_NONE{0}; +/// \brief A special id used to indicate an optional id not being specified. +IkarusId const IKARUS_ID_UNSPECIFIED{1}; + +/// \private \brief Generates a new id for the given object type. +/// \param object_type The type of the object to generate an id for. +/// \return The generated id. +IkarusId ikarus_id_from_integer(IkarusObjectType object_type); + +/// \brief Fetches the object type of the given id. +/// \param id The id to fetch the object type for. +/// \return The object type of the given id. +IKA_API IkarusObjectType ikarus_id_get_object_type(IkarusId id); + +/// \brief Checks if the given id is IKARUS_ID_NONE. +/// \param id The id to check. +/// \return True if the id is IKARUS_ID_NONE, false otherwise. +IKA_API bool ikarus_id_is_none(IkarusId id); + +/// \brief Checks if the given id is IKARUS_ID_UNSPECIFIED. +/// \param id The id to check. +/// \return True if the id is IKARUS_ID_UNSPECIFIED, false otherwise. +IKA_API bool ikarus_id_is_unspecified(IkarusId id); + +/// @} + +IKARUS_END_HEADER \ No newline at end of file diff --git a/include/ikarus/types/object.h b/include/ikarus/types/object.h new file mode 100644 index 0000000..9d8b9bd --- /dev/null +++ b/include/ikarus/types/object.h @@ -0,0 +1,164 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_OBJECT_TYPES +// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION +// IMPLEMENTATION_DETAIL_PROPERTY_TYPES + +/// \file id.h +/// \author Folling + +/// \defgroup object Objects +/// \brief Objects are a compound type of all types of objects in the database. +/// \details The following objects currently exist: +/// - blueprints +/// - properties +/// - entities +/// - blueprint folders +/// - property folders +/// - entity folders +/// @{ + +#include + +IKARUS_BEGIN_HEADER + +/// \brief A blueprint object. +/// \details A blueprint is a collection of properties which can be linked to entities. +/// Each entity the blueprint is linked to will have values for the blueprints properties. +struct IkarusBlueprint { + IkarusId id; +}; + +/// \brief Properties are the placeholders of values for entities. +/// \details Each entity can have any number of properties. +/// Every property has a type that identifies the kind of data that can be put in. +/// +/// 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) +/// +/// Every property has settings which can be used to customise the property further. +/// Two settings that are shared among all properties are the following: +/// - Multiple +/// - Allow undefined +/// +/// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. +/// The latter allows you to specify an "unknown" value for a property. +/// It might not be known if a character is dead or not for example. +/// +/// Each entity associated with the property has a value for it. +/// +/// Properties can also be added to blueprints in which case they are available for all entities associated with the +/// blueprint. +/// +/// We call properties within entities "Entity Properties" and properties within blueprints "Blueprint Properties". +/// +/// \remark Values for properties are lazily created as space saving measure. +/// Fetching the value for some property of some entity will return the property's default value if none is specified. +/// This default value is specified when the property is created and can be updated later. +/// +/// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. +struct IkarusProperty { + /// \private \brief The ID of the property. + IkarusId id; +}; + +/// \brief Entities are the core building blocks of Ikarus. +/// \detials Blueprints and Properties define the structure of the data. +/// Entities define the data itself. +/// +/// Properties can be associated with Entities in two ways: +/// - Directly: The property is linked to the entity. +/// - Indirectly: The property is linked to a blueprint of the entity. +/// +/// For each property an entity is linked to, it has a value. These values depend on the property's type. +/// For more information on the types see the property documentation. +/// +/// Values are the core type of data within Ikarus. +/// Each value is associated with one page and one property. +/// +/// \remark Values are typed, the type of a value is specified by its associated property. +/// For more information on the types see the property documentation. +/// +/// \remark Values are guaranteed to be in valid format for a given type +/// but not guaranteed to be valid under the settings of the property. +/// This is because changing the settings can invalidate existing values without resetting them. +struct IkarusEntity { + /// \private \brief The ID of the entity. + IkarusId id; +}; + +/// \brief A blueprint folder. +/// \see Folder +struct IkarusBlueprintFolder { + /// \private \brief The ID of the folder. + IkarusId id; +}; + +/// \brief A property folder. +/// \remark Property folders are scoped to the blueprint or entity they are associated with. +/// \see Folder +struct IkarusPropertyFolder { + /// \private \brief The ID of the folder. + IkarusId id; +}; + +/// \brief An entity folder. +/// \see Folder +struct IkarusEntityFolder { + /// \private \brief The ID of the folder. + IkarusId id; +}; + +/// \private \brief The data of a folder. +union IkarusFolderData { + /// \private \brief The blueprint folder data of the folder. + IkarusBlueprintFolder blueprint_folder; + /// \private \brief The property folder data of the folder. + IkarusPropertyFolder property_folder; + /// \private \brief The entity folder data of the folder. + IkarusEntityFolder entity_folder; +}; + +/// \brief A generic folder. Similar to how Objects wrap all types of objects, Folders wrap all types of folders. +struct IkarusFolder { + /// \private \brief The data of the folder. + IkarusFolderData data; + + /// \private \brief The type of the folder. + IkarusFolderType type; +}; + +/// \private \brief The data of an object. +union IkarusObjectData { + /// \private \brief The blueprint data of the object. + IkarusBlueprint blueprint; + /// \private \brief The property data of the object. + IkarusProperty property; + /// \private \brief The entity data of the object. + IkarusEntity entity; + /// \private \brief The blueprint folder data of the object. + IkarusBlueprintFolder blueprint_folder; + /// \private \brief The property folder data of the object. + IkarusPropertyFolder property_folder; + /// \private \brief The entity folder data of the object. + IkarusEntityFolder entity_folder; +}; + +/// \brief A generic object. Wraps all types of objects, including folders. +struct IkarusObject { + /// \private \brief The data of the object. + IkarusObjectData data; + /// \private \brief The type of the object. + IkarusObjectType type; +}; + +// @} + +IKARUS_END_HEADER diff --git a/include/ikarus/types/object_scope.h b/include/ikarus/types/object_scope.h new file mode 100644 index 0000000..1fe51e1 --- /dev/null +++ b/include/ikarus/types/object_scope.h @@ -0,0 +1,149 @@ +// IMPLEMENTATION_DETAIL_OBJECT_SCOPES, IMPLEMENTATION_DETAIL_TREE_LAYOUT + +/// \file object_scope.h +/// \author Folling + +/// \defgroup object_scopes Object Scopes +/// \brief Scopes define where objects belong to. +/// \details They are required to differentiate between different types of objects with NULL as their parent. +/// @{ + +#pragma once + +#include +#include + +IKARUS_BEGIN_HEADER + +/// \brief The global scope of all blueprints. +struct IkarusBlueprintScope { + /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. + short _dummy; +}; + +/// \brief Data for a property scope. This can either be a blueprint or an entity. +union IkarusPropertyScopeData { + /// \private \brief The blueprint the property is scoped to. + IkarusBlueprint _blueprint; + /// \private \brief The entity the property is scoped to. + IkarusEntity _entity; +}; + +/// \brief The type of a property scope. This can either be a blueprint or an entity. +enum IkarusPropertyScopeType { + /// \brief The property is scoped to a blueprint. + IkarusPropertyScopeType_Blueprint, + /// \brief The property is scoped to an entity. + IkarusPropertyScopeType_Entity +}; + +/// \brief The scope of a property +struct IkarusPropertyScope { + /// \private \brief Represents the type of the scope. + IkarusPropertyScopeType _type; + /// \private \brief Represents the data of the scope. + IkarusPropertyScopeData _data; +}; + +/// The global scope of all entities. +struct IkarusEntityScope { + /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. + short _dummy; +}; + +/// \private \brief The data for an object scope. +union IkarusObjectScopeData { + /// \private \brief The blueprint data of the scope. + IkarusBlueprintScope _blueprint; + /// \private \brief The property data of the scope. + IkarusPropertyScope _property; + /// \private \brief The entity data of the scope. + IkarusEntityScope _entity; +}; + +/// The type of an object scope. +enum IkarusObjectScopeType { + /// \brief The scope is a blueprint scope. + IkarusObjectScopeType_Blueprint, + /// \brief The scope is a property scope. + IkarusObjectScopeType_Property, + /// \brief The scope is an entity scope. + IkarusObjectScopeType_Entity +}; + +/// \brief The scope of an object. +struct IkarusObjectScope { + /// \private \brief Represents the type of the scope. + IkarusObjectScopeType _type; + /// \private \brief Represents the data of the scope. + IkarusObjectScopeData _data; +}; + +/// \brief Creates a blueprint scope. +/// \return The created blueprint scope. +IKA_API IkarusBlueprintScope ikarus_blueprint_scope_create(); +/// \brief Converts a blueprint scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope); + +/// \brief Creates a property scope from a blueprint. +/// \param blueprint The blueprint the property is scoped to. +/// \return The created property scope. +IKA_API IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint const * blueprint); +/// \brief Creates a property scope from a entity. +/// \param entity The entity the property is scoped to. +/// \return The created property scope. +IKA_API IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity const * entity); +/// \brief Converts a property scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope); + +/// \brief Fetches the type of an property scope. +/// \param scope The scope to fetch the type of. +/// \return The type of the scope. +IKA_API IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope const * scope); + +/// \brief Visits a property scope, calling the appropriate function. +/// \param scope The scope to to visit +/// \param blueprint The function to call if the property is scoped to a blueprint. +/// \param entity The function to call if the property is scoped to an entity. +/// \param data Optional data to pass to the functions. +void ikarus_property_scope_visit( + IkarusPropertyScope const * scope, + void (*blueprint)(IkarusBlueprint const *, void *), + void (*entity)(IkarusEntity const *, void *), + void * data +); + +/// \brief Creates an entity scope. +/// \return The created entity scope. +IKA_API IkarusEntityScope ikarus_entity_scope_create(); +/// Converts an entity scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope); + +/// \brief Fetches the type of an object scope. +/// \param scope The scope to fetch the type of. +/// \return The type of the scope. +IKA_API IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope const * scope); + +/// \brief Visits an object scope, calling the appropriate function. +/// \param scope The scope to visit. +/// \param blueprint The function to call if the scope is an #IkarusBlueprintScope. +/// \param property The function to call if the scope is an #IkarusPropertyScope. +/// \param entity The function to call if the scope is an #IkarusEntityScope. +/// \remark function pointers may be null in which case they are not called. +IKA_API void ikarus_object_scope_visit( + IkarusObjectScope const * scope, + void (*blueprint)(IkarusBlueprintScope const *, void *), + void (*property)(IkarusPropertyScope const *, void *), + void (*entity)(IkarusEntityScope const *, void *), + void * data +); + +/// @} + +IKARUS_END_HEADER diff --git a/include/ikarus/types/object_type.h b/include/ikarus/types/object_type.h new file mode 100644 index 0000000..ca3dfb0 --- /dev/null +++ b/include/ikarus/types/object_type.h @@ -0,0 +1,63 @@ +#pragma once + +#include + +IKARUS_BEGIN_HEADER + +/// \defgroup object_types ObjectTypes +/// \brief ObjectTypes are used to identify the type of objects. +/// @{ + +/// \brief The type of a folder. +/// \remark folders have the first bit set and then mirror the object type of the underlying object +enum IkarusFolderType { + /// \brief Not a folder or no folder. + IkarusFolderType_None = 0, + /// \brief An IkarusBlueprintFolder + IkarusFolderType_BlueprintFolder = 0b1000'0001, + /// \brief An IkarusPropertyFolder + IkarusFolderType_PropertyFolder = 0b1000'0010, + /// \brief An IkarusEntityFolder + IkarusFolderType_EntityFolder = 0b1000'0011, +}; + +/// \brief The type of an object. +/// \remark folders have the first bit set and then mirror the object type of the underlying object +enum IkarusObjectType { + /// \brief Not an object or no object. + ObjectType_None = 0, + /// \brief An IkarusBlueprint. + ObjectType_Blueprint = 0b0000'0001, + /// \brief An IkarusProperty. + ObjectType_Property = 0b0000'0010, + /// \brief An IkarusEntity. + ObjectType_Entity = 0b0000'0011, + /// \brief An IkarusBlueprintFolder + ObjectType_BlueprintFolder = 0b1000'0001, + /// \brief An IkarusPropertyFolder + ObjectType_PropertyFolder = 0b1000'0010, + /// \brief An IkarusEntityFolder + ObjectType_EntityFolder = 0b1000'0011, +}; + +/// \brief A bitset of IkarusObjectType%s. +enum ObjectTypes { + /// \brief No object type. + ObjectTypes_None = 0, + /// \brief An IkarusBlueprint. + ObjectTypes_Blueprint = 1 << ObjectType_Blueprint, + /// \brief An IkarusProperty. + ObjectTypes_Property = 1 << ObjectType_Property, + /// \brief An IkarusEntity. + ObjectTypes_Entity = 1 << ObjectType_Entity, + /// \brief An IkarusBlueprintFolder + ObjectTypes_BlueprintFolder = 1 << ObjectType_BlueprintFolder, + /// \brief An IkarusPropertyFolder + ObjectTypes_PropertyFolder = 1 << ObjectType_PropertyFolder, + /// \brief An IkarusEntityFolder + ObjectTypes_EntityFolder = 1 << ObjectType_EntityFolder, +}; + +// @} + +IKARUS_END_HEADER \ No newline at end of file diff --git a/include/ikarus/types/property_type.h b/include/ikarus/types/property_type.h new file mode 100644 index 0000000..d22e1a6 --- /dev/null +++ b/include/ikarus/types/property_type.h @@ -0,0 +1,31 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_PROPERTY_TYPES + +/// \file id.h +/// \author Folling + +#include + +IKARUS_BEGIN_HEADER + +/// \defgroup property_types Property Types +/// \brief Property Types delineate the type of data stored by a property. +/// @{ + +/// \brief The type of a property. +/// \details Designates the type of data stored by the property as well as which settings are +/// available. +/// \see IkarusPropertySettings +enum IkarusPropertyType { + /// \brief A true/false boolean-like value. + IkarusPropertyType_Toggle, + /// \brief An arbitrary numeric value. + IkarusPropertyType_Number, + /// \brief An arbitrary textual value. + IkarusPropertyType_Text, +}; + +/// @} + +IKARUS_END_HEADER diff --git a/include/ikarus/types/value.h b/include/ikarus/types/value.h new file mode 100644 index 0000000..d636d27 --- /dev/null +++ b/include/ikarus/types/value.h @@ -0,0 +1,161 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_PROPERTY_TYPES + +#include +#include +#include + +IKARUS_BEGIN_HEADER + +/// \defgroup entity_value Entity Values +/// \brief The values stored in entities. +/// \details Each entity has a value for each property it is associated with. +/// The value is of the type specified by the property and constrained by the property's settings. +/// \see PropertyType PropertySettings +/// @{ + +/// \brief A true/false boolean-like value. For example "IsDead". +struct IkarusToggleValue { + /// \private \brief The value of the property. + bool _value; +}; + +/// \brief An arbitrary numeric value. For example "Age". +struct IkarusNumberValue { + /// \private \brief The value of the property. + long double _value; +}; + +/// \brief An arbitrary textual value. For example "First Name". +struct IkarusTextValue { + /// \private \brief The value of the property. + char const * _value; +}; + +/// \private \brief The data for a value. +union IkarusEntityValueData { + /// \private \brief The value as a toggle. + IkarusToggleValue toggle; + /// \private \brief The value as a number. + IkarusNumberValue number; + /// \private \brief The value as text. + IkarusTextValue text; +}; + +/// \brief The state of an entity value. +/// \details States provide insight into the nature of a value. +enum IkarusEntityValueState { + /// \brief The value is invalid. + IkarusEntityValueState_Invalid, + /// \brief The value is normal and can be used as-is. + IkarusEntityValueState_Normal, + /// \brief The value is unknown. + IkarusEntityValueState_Indeterminate, +}; + +/// \brief The value of an entity associated with a property. +struct IkarusEntityValue { + /// \private \brief The type of the value. + IkarusPropertyType _type; + /// \private \brief The data for the value.p + IkarusEntityValueData _data; + /// \private \brief The state of the value. + IkarusEntityValueState _state; +}; + +/// \brief Creates an entity value from a toggle value. +/// \param value The toggle value. +/// \return The entity value. +IKA_API IkarusEntityValue ikarus_value_create_toggle(bool value); +/// \brief Creates an entity value from a number value. +/// \param value The number value. +/// \return The entity value. +/// \remark If the value is NaN or infinity an InvalidEntityValue is returned. +IKA_API IkarusEntityValue ikarus_value_create_number(long double value); +/// \brief Creates an entity value from a text value. +/// \param value The text value. +/// \return The entity value. +/// \remark If the value is null an InvalidEntityValue is returned. +IKA_API IkarusEntityValue ikarus_value_create_text(char const * value); + +/// \brief Creates an indeterminate entity value of a given type. +/// \param type The type of the value. +/// \return The entity value. +IKA_API IkarusEntityValue ikarus_value_create_indeterminate(IkarusPropertyType type); + +/// \brief Fetches the default value for a property type. +/// \remark Not to be confused with the default value of a property. See ikarus_property_get_default_value +/// \param type The property type. +/// \return The default value for the property type. +IKA_API IkarusEntityValue ikarus_value_get_default(IkarusPropertyType type); + +/// \brief Fetches the underlying value of a toggle value. +/// \param value The toggle value. +/// \return The underlying value. +IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value); + +/// \brief Fetches the underlying value of a number value. +/// \param value The number value. +/// \return The underlying value. +IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value); + +/// \brief Fetches the underlying value of a text value. +/// \param value The text value. +/// \return The underlying value. +IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); + +/// \brief Checks if a toggle value is equal to a boolean. +/// \param value The toggle value. +/// \param check The boolean value. +/// \return False if value is null. True if it is equal to check, false otherwise. +IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * value, bool check); + +/// \brief Checks if a number value is equal to a number. +/// \param value The number value. +/// \param check The number value. +/// \return False if value is null. True if it is equal to check, false otherwise. +IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * value, long double check); + +/// \brief Checks if a text value is equal to a string. +/// \param value The text value. +/// \param check The string value. +/// \return False if value or check are null. True if it is equal to check, false otherwise. +IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * value, char const * check); + +/// \brief Checks if two entity values are equal. +/// \details Two entity values are equal if they are of the same type and their value is considered equal. +/// Note that floating point values can only be checked for approximate equality. +/// \param left The left-hand entity value. +/// \param right The right-hand entity value. +/// \return True if the values are considered equal, false otherwise. +/// \remark Null values compare false to all other values. As do invalid values. Indeterminate values however, compare true to +/// other indeterminate values of the same type. +IKA_API bool ikarus_value_is_equal(IkarusEntityValue const * left, IkarusEntityValue const * right); + +/// \brief Checks if an entity value is invalid. +/// \param value The entity value. +/// \return True if the value is invalid or null, false otherwise. +IKA_API bool ikarus_value_is_invalid(IkarusEntityValue const * value); + +/// \brief Fetches the type of an entity value. +/// \param value The entity value. +/// \return The type of the entity value. +IKA_API IkarusPropertyType ikarus_value_get_type(IkarusEntityValue const * value); + +/// \brief Visits an entity value, calling the appropriate function for the value's type. +/// \param value The entity value to visit. +/// \param toggle The function to call if the value is a toggle value. +/// \param number The function to call if the value is a number value. +/// \param text The function to call if the value is a text value. +/// \param data The data to pass to the functions. +/// \remark function pointers may be null in which case they are not called. +IKA_API void ikarus_value_visit( + IkarusEntityValue const * value, + void (*toggle)(IkarusToggleValue const *, void *), + void (*number)(IkarusNumberValue const *, void *), + void (*text)(IkarusTextValue const *, void *), + void * data +); + +IKARUS_END_HEADER diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..a0665aa --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,7 @@ +file( + GLOB_RECURSE + FILES + "*.cpp" +) + +set(SOURCE_FILES ${FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/types/object_scope.cpp b/src/types/object_scope.cpp new file mode 100644 index 0000000..a4f2b49 --- /dev/null +++ b/src/types/object_scope.cpp @@ -0,0 +1,209 @@ +#include "ikarus/types/object_scope.h" + +#include + +#include + +IkarusBlueprintScope ikarus_blueprint_scope_create() { + return IkarusBlueprintScope{._dummy = 0}; +} + +IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope) { + IkarusObjectScopeData data{}; + data._blueprint = *scope; + + return IkarusObjectScope{._type = IkarusObjectScopeType_Blueprint, ._data = data}; +} + +IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint const * blueprint) { + IkarusPropertyScopeData data{}; + data._blueprint = *blueprint; + return IkarusPropertyScope{._type = IkarusPropertyScopeType_Blueprint, ._data = data}; +} + +IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity const * entity) { + IkarusPropertyScopeData data{}; + data._entity = *entity; + return IkarusPropertyScope{._type = IkarusPropertyScopeType_Entity, ._data = data}; +} + +IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope) { + IkarusObjectScopeData data{}; + data._property = *scope; + + return IkarusObjectScope{._type = IkarusObjectScopeType_Property, ._data = data}; +} + +IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope const * scope) { + return scope->_type; +} + +void ikarus_property_scope_visit( + IkarusPropertyScope const * scope, + void (*blueprint)(IkarusBlueprint const *, void *), + void (*entity)(IkarusEntity const *, void *), + void * data +) { + switch (scope->_type) { + case IkarusPropertyScopeType_Blueprint: blueprint(&scope->_data._blueprint, data); break; + case IkarusPropertyScopeType_Entity: entity(&scope->_data._entity, data); break; + } +} + +IkarusEntityScope ikarus_entity_scope_create() { + return IkarusEntityScope{._dummy = 0}; +} + +IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope) { + IkarusObjectScopeData data{}; + data._entity = *scope; + + return IkarusObjectScope{._type = IkarusObjectScopeType_Entity, ._data = data}; +} + +IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope const * scope) { + return scope->_type; +} + +void ikarus_object_scope_visit( + IkarusObjectScope const * scope, + void (*blueprint)(IkarusBlueprintScope const *, void *), + void (*property)(IkarusPropertyScope const *, void *), + void (*entity)(IkarusEntityScope const *, void *), + void * data +) { + switch (scope->_type) { + case IkarusObjectScopeType_Blueprint: { + if (blueprint != nullptr) { + blueprint(&scope->_data._blueprint, data); + } + break; + } + case IkarusObjectScopeType_Property: { + if (property != nullptr) { + property(&scope->_data._property, data); + } + break; + } + case IkarusObjectScopeType_Entity: { + if (entity != nullptr) { + entity(&scope->_data._entity, data); + } + break; + } + } +} + +TEST_CASE("blueprint_object_scope_conversion", "[object_scope]") { + auto blueprint_scope = ikarus_blueprint_scope_create(); + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); + REQUIRE(blueprint_object_scope._type == IkarusObjectScopeType_Blueprint); +} + +TEST_CASE("property_scope_type", "[object_scope]") { + auto blueprint = IkarusBlueprint{}; + auto entity = IkarusEntity{}; + + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto property_entity_scope = ikarus_property_scope_create_entity(&entity); + + REQUIRE(ikarus_property_scope_get_type(&property_blueprint_scope) == IkarusPropertyScopeType_Blueprint); + REQUIRE(ikarus_property_scope_get_type(&property_entity_scope) == IkarusPropertyScopeType_Entity); +} + +TEST_CASE("property_object_scope_conversion", "[object_scope]") { + auto blueprint = IkarusBlueprint{}; + auto entity = IkarusEntity{}; + + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto property_blueprint_object_scope = ikarus_property_scope_to_object_scope(&property_blueprint_scope); + + REQUIRE(property_blueprint_object_scope._type == IkarusObjectScopeType_Property); + + auto property_entity_scope = ikarus_property_scope_create_entity(&entity); + auto property_entity_object_scope = ikarus_property_scope_to_object_scope(&property_entity_scope); + + REQUIRE(property_entity_object_scope._type == IkarusObjectScopeType_Property); +} + +TEST_CASE("property_scope_visiting", "[object_scope]") { + auto blueprint = IkarusBlueprint{}; + auto entity = IkarusEntity{}; + + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto property_entity_scope = ikarus_property_scope_create_entity(&entity); + + int test = 0; + + ikarus_property_scope_visit( + &property_blueprint_scope, + [](IkarusBlueprint const * _, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusEntity const * _, void * data) { *reinterpret_cast(data) = 2; }, + &test + ); + + REQUIRE(test == 1); + + ikarus_property_scope_visit( + &property_entity_scope, + [](IkarusBlueprint const * _, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusEntity const * _, void * data) { *reinterpret_cast(data) = 2; }, + &test + ); + + REQUIRE(test == 2); +} + +TEST_CASE("entity_object_scope_conversion", "[object_scope]") { + auto entity_scope = ikarus_entity_scope_create(); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + REQUIRE(entity_object_scope._type == IkarusObjectScopeType_Entity); +} + +TEST_CASE("object_scope_type_fetching", "[object_scope]") { + auto blueprint = IkarusBlueprint{}; + + auto blueprint_scope = ikarus_blueprint_scope_create(); + auto property_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto entity_scope = ikarus_entity_scope_create(); + + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); + auto property_object_scope = ikarus_property_scope_to_object_scope(&property_scope); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + + REQUIRE(ikarus_object_scope_get_type(&blueprint_object_scope) == IkarusObjectScopeType_Blueprint); + REQUIRE(ikarus_object_scope_get_type(&property_object_scope) == IkarusObjectScopeType_Property); + REQUIRE(ikarus_object_scope_get_type(&entity_object_scope) == IkarusObjectScopeType_Entity); +} + +TEST_CASE("object_scope_visiting", "[object_scope]") { + auto blueprint = IkarusBlueprint{}; + + auto blueprint_scope = ikarus_blueprint_scope_create(); + auto property_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto entity_scope = ikarus_entity_scope_create(); + + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); + auto property_object_scope = ikarus_property_scope_to_object_scope(&property_scope); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + + auto scopes = { + std::make_pair(blueprint_object_scope, 1), + std::make_pair(property_object_scope, 2), + std::make_pair(entity_object_scope, 3), + }; + + for (auto [scope, value] : scopes) { + int test = 0; + + ikarus_object_scope_visit( + &scope, + [](IkarusBlueprintScope const * _, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusPropertyScope const * _, void * data) { *reinterpret_cast(data) = 2; }, + [](IkarusEntityScope const * _, void * data) { *reinterpret_cast(data) = 3; }, + &test + ); + + REQUIRE(test == value); + } +} diff --git a/src/types/value.cpp b/src/types/value.cpp new file mode 100644 index 0000000..251db2c --- /dev/null +++ b/src/types/value.cpp @@ -0,0 +1,337 @@ +#include "ikarus/types/value.h" + +#include +#include + +#include + +/// \brief Creates an indeterminate entity value of a given type. +/// \param type The type of the value. +/// \return The entity value. +IKA_API IkarusEntityValue value_create_invalid(IkarusPropertyType type) { + return IkarusEntityValue{ + ._type = type, + ._data = IkarusEntityValueData{}, + ._state = IkarusEntityValueState_Invalid, + }; +} + +IkarusEntityValue ikarus_value_create_toggle(bool value) { + return IkarusEntityValue{ + ._type = IkarusPropertyType_Toggle, + ._data = IkarusEntityValueData{.toggle = IkarusToggleValue{._value = value}}, + ._state = IkarusEntityValueState_Normal, + }; +} + +IkarusEntityValue ikarus_value_create_number(long double value) { + if (auto fp_class = std::fpclassify(value); fp_class != FP_NORMAL && fp_class != FP_ZERO) { + return value_create_invalid(IkarusPropertyType_Number); + } + + return IkarusEntityValue{ + ._type = IkarusPropertyType_Number, + ._data = IkarusEntityValueData{.number = IkarusNumberValue{._value = value}}, + ._state = IkarusEntityValueState_Normal, + }; +} + +IkarusEntityValue ikarus_value_create_text(char const * value) { + if (value == nullptr) { + return value_create_invalid(IkarusPropertyType_Text); + }; + + return IkarusEntityValue{ + ._type = IkarusPropertyType_Text, + ._data = IkarusEntityValueData{.text = IkarusTextValue{._value = value}}, + ._state = IkarusEntityValueState_Normal, + }; +} + +IkarusEntityValue ikarus_value_create_indeterminate(IkarusPropertyType type) { + IkarusEntityValueData data{}; + + switch (type) { + case IkarusPropertyType_Toggle: { + data.toggle = IkarusToggleValue{._value = false}; + break; + } + case IkarusPropertyType_Number: { + data.number = IkarusNumberValue{._value = 0.0}; + break; + } + case IkarusPropertyType_Text: { + data.text = IkarusTextValue{._value = ""}; + break; + } + default: return value_create_invalid(type); + } + + return IkarusEntityValue{ + ._type = type, + ._data = data, + ._state = IkarusEntityValueState_Indeterminate, + }; +} + +IkarusEntityValue ikarus_value_get_default(IkarusPropertyType type) { + switch (type) { + case IkarusPropertyType_Toggle: return ikarus_value_create_toggle(false); + case IkarusPropertyType_Number: return ikarus_value_create_number(0.0); + case IkarusPropertyType_Text: return ikarus_value_create_text(""); + default: return value_create_invalid(type); + } +} + +bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value) { + return value->_value; +} + +long double ikarus_number_value_get_underlying(IkarusNumberValue const * value) { + return value->_value; +} + +char const * ikarus_text_value_get_underlying(IkarusTextValue const * value) { + return value->_value; +} + +// no need to check for validity here, since these concrete types are only created by the library +bool ikarus_toggle_value_is_equal(IkarusToggleValue const * value, bool check) { + return value != nullptr && value->_value == check; +} + +bool ikarus_number_value_is_equal(IkarusNumberValue const * value, long double check) { + return value != nullptr && value->_value == check; +} + +bool ikarus_text_value_is_equal(IkarusTextValue const * value, char const * check) { + return value != nullptr && check != nullptr && std::strcmp(value->_value, check) == 0; +} + +bool ikarus_value_is_equal(IkarusEntityValue const * left, IkarusEntityValue const * right) { + if (left == nullptr || right == nullptr) { + return false; + } + + if (left->_state == IkarusEntityValueState_Invalid || right->_state == IkarusEntityValueState_Invalid) { + return false; + } + + if (left->_type != right->_type) { + return false; + } + + // indeterminate values are only equal if they have the same type + if (left->_state == IkarusEntityValueState_Indeterminate && right->_state == IkarusEntityValueState_Indeterminate) { + return true; + } + + switch (left->_type) { + case IkarusPropertyType_Toggle: return left->_data.toggle._value == right->_data.toggle._value; + case IkarusPropertyType_Number: return left->_data.number._value == right->_data.number._value; + case IkarusPropertyType_Text: return std::strcmp(left->_data.text._value, right->_data.text._value) == 0; + default: return false; + } +} + +bool ikarus_value_is_invalid(IkarusEntityValue const * value) { + return value == nullptr || value->_state == IkarusEntityValueState_Invalid; +} + +IkarusPropertyType ikarus_value_get_type(IkarusEntityValue const * value) { + return value->_type; +} + +void ikarus_value_visit( + IkarusEntityValue const * value, + void (*toggle)(IkarusToggleValue const * value, void * data), + void (*number)(IkarusNumberValue const * value, void * data), + void (*text)(IkarusTextValue const * value, void * data), + void * data +) { + if (value == nullptr) { + return; + } + + switch (value->_type) { + case IkarusPropertyType_Toggle: { + if (toggle != nullptr) { + toggle(&value->_data.toggle, data); + } + break; + } + case IkarusPropertyType_Number: { + if (number != nullptr) { + number(&value->_data.number, data); + } + break; + } + case IkarusPropertyType_Text: { + if (text != nullptr) { + text(&value->_data.text, data); + } + break; + } + default: break; + } +} + +TEST_CASE("toggle_value_creation", "[value]") { + auto toggle_value = ikarus_value_create_toggle(true); + + REQUIRE(ikarus_value_get_type(&toggle_value) == IkarusPropertyType_Toggle); + REQUIRE(ikarus_toggle_value_is_equal(&toggle_value._data.toggle, true)); +} + +TEST_CASE("number_value_creation", "[value]") { + auto number_value = ikarus_value_create_number(1.0); + + REQUIRE(ikarus_value_get_type(&number_value) == IkarusPropertyType_Number); + REQUIRE(ikarus_number_value_is_equal(&number_value._data.number, 1.0)); + + auto nan_value = ikarus_value_create_number(std::numeric_limits::quiet_NaN()); + REQUIRE(ikarus_value_is_invalid(&nan_value)); + auto signaling_non_value = ikarus_value_create_number(std::numeric_limits::signaling_NaN()); + REQUIRE(ikarus_value_is_invalid(&signaling_non_value)); + auto inf_value = ikarus_value_create_number(std::numeric_limits::infinity()); + REQUIRE(ikarus_value_is_invalid(&inf_value)); + auto neg_inf_value = ikarus_value_create_number(-std::numeric_limits::infinity()); + REQUIRE(ikarus_value_is_invalid(&neg_inf_value)); +} + +TEST_CASE("text_value_creation", "[value]") { + auto text_value = ikarus_value_create_text("test"); + + REQUIRE(ikarus_value_get_type(&text_value) == IkarusPropertyType_Text); + REQUIRE(ikarus_text_value_is_equal(&text_value._data.text, "test")); + + auto null_value = ikarus_value_create_text(nullptr); + REQUIRE(ikarus_value_is_invalid(&null_value)); +} + +TEST_CASE("default_value_creation", "[value]") { + auto types = { + IkarusPropertyType_Toggle, + IkarusPropertyType_Number, + IkarusPropertyType_Text, + }; + + for (auto type : types) { + auto value = ikarus_value_get_default(type); + REQUIRE(ikarus_value_get_type(&value) == type); + } +} + +TEST_CASE("toggle_value_underlying", "[value]") { + auto true_toggle_value = ikarus_value_create_toggle(true); + auto false_toggle_value = ikarus_value_create_toggle(false); + + REQUIRE(ikarus_toggle_value_get_underlying(&true_toggle_value._data.toggle) == true); + REQUIRE(ikarus_toggle_value_get_underlying(&false_toggle_value._data.toggle) == false); +} + +TEST_CASE("number_value_underlying", "[value]") { + auto zero_number_value = ikarus_value_create_number(0.0); + auto third_number_value = ikarus_value_create_number(1.0 / 3.0); + auto large_number_value = ikarus_value_create_number(1.2345678910e123); + + REQUIRE(ikarus_number_value_get_underlying(&zero_number_value._data.number) == 0.0); + REQUIRE(ikarus_number_value_get_underlying(&third_number_value._data.number) == 1.0 / 3.0); + REQUIRE(ikarus_number_value_get_underlying(&large_number_value._data.number) == 1.2345678910e123); +} + +TEST_CASE("text_value_underlying", "[value]") { + auto test_value = ikarus_value_create_text("test"); + auto empty_value = ikarus_value_create_text(""); + + REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&test_value._data.text), "test") == 0); + REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&empty_value._data.text), "") == 0); +} + +TEST_CASE("toggle_comparison", "[value]") { + auto true_toggle_value = ikarus_value_create_toggle(true); + auto false_toggle_value = ikarus_value_create_toggle(false); + + REQUIRE(ikarus_toggle_value_is_equal(&true_toggle_value._data.toggle, true)); + REQUIRE(ikarus_toggle_value_is_equal(&false_toggle_value._data.toggle, false)); +} + +TEST_CASE("number_comparison", "[value]") { + auto zero_number_value = ikarus_value_create_number(0.0); + auto third_number_value = ikarus_value_create_number(1.0 / 3.0); + auto large_number_value = ikarus_value_create_number(1.2345678910e123); + + REQUIRE(ikarus_number_value_is_equal(&zero_number_value._data.number, 0.0)); + REQUIRE(ikarus_number_value_is_equal(&third_number_value._data.number, 1.0 / 6.0 + 1.0 / 6.0)); + REQUIRE(ikarus_number_value_is_equal(&large_number_value._data.number, 1.2345678910e123)); +} + +TEST_CASE("text_comparison", "[value]") { + auto test_value = ikarus_value_create_text("test"); + auto empty_value = ikarus_value_create_text(""); + + REQUIRE(ikarus_text_value_is_equal(&test_value._data.text, "test")); + REQUIRE(ikarus_text_value_is_equal(&empty_value._data.text, "")); +} + +TEST_CASE("value_comparison", "[value]") { + auto true_toggle_value = ikarus_value_create_toggle(true); + auto false_toggle_value = ikarus_value_create_toggle(false); + auto number_value1 = ikarus_value_create_number(0.0); + auto number_value2 = ikarus_value_create_number(0.0); + auto invalid_value = ikarus_value_create_text(nullptr); + + auto indeterminate_toggle = ikarus_value_create_indeterminate(IkarusPropertyType_Toggle); + auto indeterminate_number1 = ikarus_value_create_indeterminate(IkarusPropertyType_Number); + auto indeterminate_number2 = ikarus_value_create_indeterminate(IkarusPropertyType_Number); + + REQUIRE(!ikarus_value_is_equal(nullptr, nullptr)); + REQUIRE(!ikarus_value_is_equal(&true_toggle_value, nullptr)); + REQUIRE(!ikarus_value_is_equal(nullptr, &true_toggle_value)); + + REQUIRE(!ikarus_value_is_equal(&invalid_value, &invalid_value)); + REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &invalid_value)); + REQUIRE(!ikarus_value_is_equal(&invalid_value, &true_toggle_value)); + + REQUIRE(ikarus_value_is_equal(&true_toggle_value, &true_toggle_value)); + REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &false_toggle_value)); + REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &number_value1)); + REQUIRE(!ikarus_value_is_equal(&number_value1, &true_toggle_value)); + REQUIRE(ikarus_value_is_equal(&number_value1, &number_value2)); + + REQUIRE(!ikarus_value_is_equal(&indeterminate_toggle, &indeterminate_number1)); + REQUIRE(ikarus_value_is_equal(&indeterminate_number1, &indeterminate_number2)); +} + +TEST_CASE("invalid_value", "[value]") { + auto invalid_value = ikarus_value_create_toggle(false); + invalid_value._state = IkarusEntityValueState_Invalid; + + REQUIRE(ikarus_value_is_invalid(&invalid_value)); +} + +TEST_CASE("visit_value", "[value]") { + auto toggle_value = ikarus_value_create_toggle(true); + auto number_value = ikarus_value_create_number(0.0); + auto text_value = ikarus_value_create_text("test"); + + auto values = { + std::make_pair(toggle_value, 1), + std::make_pair(number_value, 2), + std::make_pair(text_value, 3), + }; + + for (auto [value, expected] : values) { + int test = 0; + + ikarus_value_visit( + &value, + [](IkarusToggleValue const * _, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusNumberValue const * _, void * data) { *reinterpret_cast(data) = 2; }, + [](IkarusTextValue const * _, void * data) { *reinterpret_cast(data) = 3; }, + &test + ); + + REQUIRE(test == expected); + } +} diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt new file mode 100644 index 0000000..9820477 --- /dev/null +++ b/vendor/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(catch2) +add_subdirectory(sqlitecpp) diff --git a/vendor/catch2 b/vendor/catch2 new file mode 160000 index 0000000..5bba3e4 --- /dev/null +++ b/vendor/catch2 @@ -0,0 +1 @@ +Subproject commit 5bba3e4038602badb691da914523f667a2dd1f27 diff --git a/vendor/doxygen-awesome-css b/vendor/doxygen-awesome-css new file mode 160000 index 0000000..00a52f6 --- /dev/null +++ b/vendor/doxygen-awesome-css @@ -0,0 +1 @@ +Subproject commit 00a52f6c74065ffbd836cbd791ddfe8edf2836b8 diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp new file mode 160000 index 0000000..afe7b16 --- /dev/null +++ b/vendor/sqlitecpp @@ -0,0 +1 @@ +Subproject commit afe7b165002ccf86a37da5b6b157ce4ff9db0401 From d0f02d7eccb7d1e7b838720b3aa0e1e1ba9c4dfe Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 22 Aug 2023 20:37:22 +0200 Subject: [PATCH 002/166] add license Signed-off-by: Folling --- LICENSE.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..598a8d4 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,10 @@ +Copyright 2019-2023 Folling (mail@folling.io) + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + From 27d603519b406c8026ffd4f48ab3481878b532d6 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 24 Aug 2023 16:40:54 +0200 Subject: [PATCH 003/166] id interface Signed-off-by: Folling --- include/ikarus/stdtypes.h | 1 + include/ikarus/types/id.h | 19 +++++++--- include/ikarus/types/object_type.h | 28 +++++++-------- src/types/id.cpp | 56 ++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 src/types/id.cpp diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h index 2f467cf..d5f7ab2 100644 --- a/include/ikarus/stdtypes.h +++ b/include/ikarus/stdtypes.h @@ -1,6 +1,7 @@ #pragma once #ifdef __cplusplus +#include #include using std::size_t; #else diff --git a/include/ikarus/types/id.h b/include/ikarus/types/id.h index a205e43..e2ca0b5 100644 --- a/include/ikarus/types/id.h +++ b/include/ikarus/types/id.h @@ -6,10 +6,10 @@ #pragma once #include +#include IKARUS_BEGIN_HEADER -#include #include /// \defgroup id Ids @@ -21,11 +21,13 @@ IKARUS_BEGIN_HEADER /// \brief A wrapper around a 64 bit integer that represents the id of an object. /// \details They are stored as 64 bit integers with the following layout: -/// - first 8 bits: #IkarusObjectType - 255 possible values, 0 for special values +/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. +/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. +/// - next 7 bits: #IkarusObjectType - 127 possible values, 0 for special values /// - last 56 bits: incremented counter generated by the database struct IkarusId { /// \private \brief The value of the id. - uint64_t value; + int64_t data; }; /// \brief A special id returned by failed functions. @@ -34,9 +36,16 @@ IkarusId const IKARUS_ID_NONE{0}; IkarusId const IKARUS_ID_UNSPECIFIED{1}; /// \private \brief Generates a new id for the given object type. -/// \param object_type The type of the object to generate an id for. +/// \param data The data from which the id will be constructed. /// \return The generated id. -IkarusId ikarus_id_from_integer(IkarusObjectType object_type); +/// \pre data must be valid under the format described in Id. It should also point to an object in the database. +IkarusId ikarus_id_from_data(int64_t data); + +/// \brief Checkes whether two ids are equal +/// \param left the left side of the comparison +/// \param right the right side of the comparison +/// \return True if the bits from the left id are equal to the bits of the right id +bool ikarus_id_is_equal(IkarusId left, IkarusId right); /// \brief Fetches the object type of the given id. /// \param id The id to fetch the object type for. diff --git a/include/ikarus/types/object_type.h b/include/ikarus/types/object_type.h index ca3dfb0..1c0cf9d 100644 --- a/include/ikarus/types/object_type.h +++ b/include/ikarus/types/object_type.h @@ -25,37 +25,37 @@ enum IkarusFolderType { /// \remark folders have the first bit set and then mirror the object type of the underlying object enum IkarusObjectType { /// \brief Not an object or no object. - ObjectType_None = 0, + IkarusObjectType_None = 0, /// \brief An IkarusBlueprint. - ObjectType_Blueprint = 0b0000'0001, + IkarusObjectType_Blueprint = 0b0000'0001, /// \brief An IkarusProperty. - ObjectType_Property = 0b0000'0010, + IkarusObjectType_Property = 0b0000'0010, /// \brief An IkarusEntity. - ObjectType_Entity = 0b0000'0011, + IkarusObjectType_Entity = 0b0000'0011, /// \brief An IkarusBlueprintFolder - ObjectType_BlueprintFolder = 0b1000'0001, + IkarusObjectType_BlueprintFolder = 0b1000'0001, /// \brief An IkarusPropertyFolder - ObjectType_PropertyFolder = 0b1000'0010, + IkarusObjectType_PropertyFolder = 0b1000'0010, /// \brief An IkarusEntityFolder - ObjectType_EntityFolder = 0b1000'0011, + IkarusObjectType_EntityFolder = 0b1000'0011, }; /// \brief A bitset of IkarusObjectType%s. enum ObjectTypes { /// \brief No object type. - ObjectTypes_None = 0, + IkarusObjectTypes_None = 0, /// \brief An IkarusBlueprint. - ObjectTypes_Blueprint = 1 << ObjectType_Blueprint, + IkarusObjectTypes_Blueprint = 1 << IkarusObjectType_Blueprint, /// \brief An IkarusProperty. - ObjectTypes_Property = 1 << ObjectType_Property, + IkarusObjectTypes_Property = 1 << IkarusObjectType_Property, /// \brief An IkarusEntity. - ObjectTypes_Entity = 1 << ObjectType_Entity, + IkarusObjectTypes_Entity = 1 << IkarusObjectType_Entity, /// \brief An IkarusBlueprintFolder - ObjectTypes_BlueprintFolder = 1 << ObjectType_BlueprintFolder, + IkarusObjectTypes_BlueprintFolder = 1 << IkarusObjectType_BlueprintFolder, /// \brief An IkarusPropertyFolder - ObjectTypes_PropertyFolder = 1 << ObjectType_PropertyFolder, + IkarusObjectTypes_PropertyFolder = 1 << IkarusObjectType_PropertyFolder, /// \brief An IkarusEntityFolder - ObjectTypes_EntityFolder = 1 << ObjectType_EntityFolder, + IkarusObjectTypes_EntityFolder = 1 << IkarusObjectType_EntityFolder, }; // @} diff --git a/src/types/id.cpp b/src/types/id.cpp new file mode 100644 index 0000000..53f2689 --- /dev/null +++ b/src/types/id.cpp @@ -0,0 +1,56 @@ +#include "ikarus/types/id.h" + +#include + +IkarusId ikarus_id_from_data(int64_t data) { + return IkarusId { + .data = data + }; +} + +IkarusObjectType ikarus_id_get_object_type(IkarusId id) { + return static_cast(id.data >> 56); +} + +bool ikarus_id_is_equal(IkarusId left, IkarusId right) { + return left.data == right.data; +} + +bool ikarus_id_is_none(IkarusId id) { + return ikarus_id_is_equal(id, IKARUS_ID_NONE); +} + +bool ikarus_id_is_unspecified(IkarusId id) { + return ikarus_id_is_equal(id, IKARUS_ID_UNSPECIFIED); +} + +TEST_CASE("id_object_type", "[id]") { + auto id = ikarus_id_from_data(static_cast(IkarusObjectType_Blueprint) << 56); + + REQUIRE(ikarus_id_get_object_type(id) == IkarusObjectType_Blueprint); + REQUIRE(!ikarus_id_is_none(id) == IkarusObjectType_Blueprint); + REQUIRE(!ikarus_id_is_unspecified(id) == IkarusObjectType_Blueprint); +} + +TEST_CASE("id_equal", "[id]") { + auto id = ikarus_id_from_data(static_cast(IkarusObjectType_Blueprint) << 56); + auto copy = id; + auto third = ikarus_id_from_data(static_cast(IkarusObjectType_Property) << 56); + + REQUIRE(ikarus_id_is_equal(id, copy)); + REQUIRE(!ikarus_id_is_equal(id, third)); +} + +TEST_CASE("id_none", "[id]") { + auto id = IKARUS_ID_NONE; + + REQUIRE(ikarus_id_is_none(id)); + REQUIRE(!ikarus_id_is_unspecified(id)); +} + +TEST_CASE("id_unspecified", "[id]") { + auto id = IKARUS_ID_UNSPECIFIED; + + REQUIRE(!ikarus_id_is_none(id)); + REQUIRE(ikarus_id_is_unspecified(id)); +} From d33190b204061c0dc4ca2bc4e577f6a44d33b802 Mon Sep 17 00:00:00 2001 From: Folling Date: Fri, 25 Aug 2023 08:52:34 +0200 Subject: [PATCH 004/166] object type interface Signed-off-by: Folling --- include/ikarus/types/object_type.h | 36 +++++++++++++++++++----------- src/types/object_type.cpp | 32 ++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 src/types/object_type.cpp diff --git a/include/ikarus/types/object_type.h b/include/ikarus/types/object_type.h index 1c0cf9d..fd8685a 100644 --- a/include/ikarus/types/object_type.h +++ b/include/ikarus/types/object_type.h @@ -9,39 +9,40 @@ IKARUS_BEGIN_HEADER /// @{ /// \brief The type of a folder. -/// \remark folders have the first bit set and then mirror the object type of the underlying object +/// \remark These values are identical to the associated values of IkarusObjectType. enum IkarusFolderType { /// \brief Not a folder or no folder. IkarusFolderType_None = 0, /// \brief An IkarusBlueprintFolder - IkarusFolderType_BlueprintFolder = 0b1000'0001, + IkarusFolderType_BlueprintFolder = 17, /// \brief An IkarusPropertyFolder - IkarusFolderType_PropertyFolder = 0b1000'0010, + IkarusFolderType_PropertyFolder = 18, /// \brief An IkarusEntityFolder - IkarusFolderType_EntityFolder = 0b1000'0011, + IkarusFolderType_EntityFolder = 19, }; /// \brief The type of an object. -/// \remark folders have the first bit set and then mirror the object type of the underlying object +/// \remark Folders have the 4th bit set. enum IkarusObjectType { /// \brief Not an object or no object. IkarusObjectType_None = 0, /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 0b0000'0001, + IkarusObjectType_Blueprint = 1, /// \brief An IkarusProperty. - IkarusObjectType_Property = 0b0000'0010, + IkarusObjectType_Property = 2, /// \brief An IkarusEntity. - IkarusObjectType_Entity = 0b0000'0011, + IkarusObjectType_Entity = 3, /// \brief An IkarusBlueprintFolder - IkarusObjectType_BlueprintFolder = 0b1000'0001, + IkarusObjectType_BlueprintFolder = IkarusFolderType_BlueprintFolder, /// \brief An IkarusPropertyFolder - IkarusObjectType_PropertyFolder = 0b1000'0010, + IkarusObjectType_PropertyFolder = IkarusFolderType_PropertyFolder, /// \brief An IkarusEntityFolder - IkarusObjectType_EntityFolder = 0b1000'0011, + IkarusObjectType_EntityFolder = IkarusFolderType_EntityFolder, }; +// because of the nature of bitsets, the largest possible object-type is 31 /// \brief A bitset of IkarusObjectType%s. -enum ObjectTypes { +enum IkarusObjectTypes { /// \brief No object type. IkarusObjectTypes_None = 0, /// \brief An IkarusBlueprint. @@ -58,6 +59,15 @@ enum ObjectTypes { IkarusObjectTypes_EntityFolder = 1 << IkarusObjectType_EntityFolder, }; +/// \brief Converts an IkarusFolderType to an IkarusObjectType. +/// \param type The IkarusFolderType to convert. +/// \return The converted IkarusObjectType, representing the folder type. +IKA_API IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type); +/// \brief Converts an IkarusObjectType to a bitset of IkarusObjectTypes. +/// \param type The IkarusObjectType to convert. +/// \return The converted IkarusObjectTypes, representing the object type. +IKA_API IkarusObjectTypes ikarus_object_type_to_bitset(IkarusObjectType type); + // @} -IKARUS_END_HEADER \ No newline at end of file +IKARUS_END_HEADER diff --git a/src/types/object_type.cpp b/src/types/object_type.cpp new file mode 100644 index 0000000..7f5e377 --- /dev/null +++ b/src/types/object_type.cpp @@ -0,0 +1,32 @@ +#include + +#include + +IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type) { + return static_cast(type); +} + +IkarusObjectTypes ikarus_object_type_to_bitset(IkarusObjectType type) { + if (type == 0) { + return static_cast(0); + } + + return static_cast(1 << type); +} + +TEST_CASE("folder_to_object_type_conversion", "[object_type]") { + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_None) == IkarusObjectType_None); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_BlueprintFolder) == IkarusObjectType_BlueprintFolder); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_PropertyFolder) == IkarusObjectType_PropertyFolder); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_EntityFolder) == IkarusObjectType_EntityFolder); +} + +TEST_CASE("object_type_to_bitset_conversion", "[object_type]") { + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_None) == IkarusObjectTypes_None); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Blueprint) == IkarusObjectTypes_Blueprint); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Property) == IkarusObjectTypes_Property); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Entity) == IkarusObjectTypes_Entity); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_BlueprintFolder) == IkarusObjectTypes_BlueprintFolder); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_PropertyFolder) == IkarusObjectTypes_PropertyFolder); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_EntityFolder) == IkarusObjectTypes_EntityFolder); +} From 0a583b659138f726563c640e976456f8ab50c35b Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 29 Aug 2023 14:12:08 +0200 Subject: [PATCH 005/166] restructure into smaller files & add IWYU/clang-tidy Signed-off-by: Folling --- .clang-format | 51 +----- CMakeLists.txt | 54 ++++-- include/ikarus/folders/blueprint_folder.h | 22 +++ include/ikarus/folders/entity_folder.h | 22 +++ include/ikarus/folders/folder.h | 76 ++++++++ include/ikarus/folders/folder_type.h | 26 +++ include/ikarus/folders/property_folder.h | 23 +++ include/ikarus/{types => }/id.h | 5 +- include/ikarus/objects/blueprint.h | 24 +++ include/ikarus/objects/entity.h | 41 +++++ include/ikarus/objects/object.h | 123 +++++++++++++ include/ikarus/objects/object_type.h | 38 ++++ include/ikarus/objects/property.h | 73 ++++++++ .../ikarus/{types => objects}/property_type.h | 2 +- include/ikarus/scopes/blueprint_scope.h | 26 +++ include/ikarus/scopes/entity_scope.h | 26 +++ include/ikarus/scopes/object_scope.h | 84 +++++++++ include/ikarus/scopes/property_scope.h | 65 +++++++ include/ikarus/types/object.h | 164 ------------------ include/ikarus/types/object_scope.h | 149 ---------------- include/ikarus/types/object_type.h | 73 -------- include/ikarus/{types => values}/value.h | 5 +- src/{types => }/id.cpp | 8 +- src/objects/object.cpp | 27 +++ src/objects/object_type.cpp | 16 ++ src/{types => scopes}/object_scope.cpp | 130 +++++++------- src/types/object_type.cpp | 32 ---- src/{types => values}/value.cpp | 16 +- 28 files changed, 845 insertions(+), 556 deletions(-) create mode 100644 include/ikarus/folders/blueprint_folder.h create mode 100644 include/ikarus/folders/entity_folder.h create mode 100644 include/ikarus/folders/folder.h create mode 100644 include/ikarus/folders/folder_type.h create mode 100644 include/ikarus/folders/property_folder.h rename include/ikarus/{types => }/id.h (97%) create mode 100644 include/ikarus/objects/blueprint.h create mode 100644 include/ikarus/objects/entity.h create mode 100644 include/ikarus/objects/object.h create mode 100644 include/ikarus/objects/object_type.h create mode 100644 include/ikarus/objects/property.h rename include/ikarus/{types => objects}/property_type.h (96%) create mode 100644 include/ikarus/scopes/blueprint_scope.h create mode 100644 include/ikarus/scopes/entity_scope.h create mode 100644 include/ikarus/scopes/object_scope.h create mode 100644 include/ikarus/scopes/property_scope.h delete mode 100644 include/ikarus/types/object.h delete mode 100644 include/ikarus/types/object_scope.h delete mode 100644 include/ikarus/types/object_type.h rename include/ikarus/{types => values}/value.h (98%) rename src/{types => }/id.cpp (93%) create mode 100644 src/objects/object.cpp create mode 100644 src/objects/object_type.cpp rename src/{types => scopes}/object_scope.cpp (62%) delete mode 100644 src/types/object_type.cpp rename src/{types => values}/value.cpp (96%) diff --git a/.clang-format b/.clang-format index 0372fa7..513423e 100644 --- a/.clang-format +++ b/.clang-format @@ -81,72 +81,31 @@ FixNamespaceComments: false IncludeBlocks: Regroup IncludeCategories: - # Relative Includes - # "blubb.h" / "blubb/blubber.h" - Regex: '^".+\.(h|hpp)"$' Priority: 1 - - # C Includes - # - Regex: '^<[a-z0-9_]+\.h>$' Priority: 2 - - # C++ Includes - # - Regex: '^<[a-z0-9_]+>$' Priority: 3 - - # expected - # - Regex: '^$' Priority: 4 - - # libfmt - # - Regex: '^$' Priority: 5 - - # nlohmann::json - # - Regex: '^$' Priority: 6 - - # ranges - # - Regex: '^$' Priority: 7 - - # ICU - # - - Regex: '^$' + - Regex: '^$' Priority: 8 - - # ranges - # - - Regex: '^$' + - Regex: '^$' Priority: 9 - - # ranges - # - - Regex: '^$' + - Regex: '^$' Priority: 10 - - # ranges - # - - Regex: '^$' + - Regex: '^$' Priority: 11 - - # ranges - # - - Regex: '^$' + - Regex: '^$' Priority: 12 - # ranges - # ranges - # - - Regex: '^$' - Priority: 13 - IndentAccessModifiers: false IndentCaseBlocks: false IndentCaseLabels: false diff --git a/CMakeLists.txt b/CMakeLists.txt index a7faf39..5edd8b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,9 @@ cmake_minimum_required(VERSION 3.18) project(ikarus) +option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF) +option(LIBIKARUS_ENABLE_LINTS "Enable linting" OFF) + set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 20) @@ -11,7 +14,7 @@ add_subdirectory(include) add_subdirectory(src) add_library( - libikarus OBJECT + libikarus SHARED ${INCLUDE_FILES} ${SOURCE_FILES} ) @@ -27,7 +30,7 @@ target_include_directories( ) target_link_libraries( - libikarus PUBLIC + libikarus PRIVATE Catch2::Catch2WithMain ) @@ -37,19 +40,40 @@ target_link_libraries( sqlitecpp ) -set_target_properties( - libikarus PROPERTIES - LINKER_LANGUAGE CXX -) +if (LIBIKARUS_ENABLE_LINTS) + find_program(IWYU_PATH NAMES include-what-you-use iwyu REQUIRED) + find_program(CLANG_TIDY_PATH NAMES clang-tidy REQUIRED) -add_executable(ikarus_tests ${SOURCE_FILES}) -target_link_libraries(ikarus_tests PRIVATE Catch2::Catch2WithMain) + set_property(TARGET libikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) + string( + CONCAT CLANG_TIDY_OPTIONS + "-checks=-*," + "bugprone-*," + "concurrency-*," + "cppcoreguidelines-*,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-type-union-access," + "misc-*," + "modernize-*,-modernize-use-trailing-return-type," + "performance-*," + "portability-*," + "readability-*,-readability-identifier-length,-readability-magic-numbers,-readability-function-cognitive-complexity" + ) -target_include_directories( - ikarus_tests PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/include -) + set_property( + TARGET libikarus + PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY_PATH};${CLANG_TIDY_OPTIONS} + ) +endif () -include(CTest) -include(vendor/catch2/extras/Catch.cmake) -catch_discover_tests(ikarus_tests) +if (LIBIKARUS_ENABLE_TESTS) + add_executable(libikarus_tests ${SOURCE_FILES}) + target_link_libraries(libikarus_tests PRIVATE Catch2::Catch2WithMain) + + target_include_directories( + libikarus_tests PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include + ) + + include(CTest) + include(vendor/catch2/extras/Catch.cmake) + catch_discover_tests(libikarus_tests) +endif () \ No newline at end of file diff --git a/include/ikarus/folders/blueprint_folder.h b/include/ikarus/folders/blueprint_folder.h new file mode 100644 index 0000000..8ab973e --- /dev/null +++ b/include/ikarus/folders/blueprint_folder.h @@ -0,0 +1,22 @@ +#pragma once + +/// \file blueprint_folder.h +/// \author Folling + +#include +#include + +/// \addtogroup folder Folders +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A blueprint folder, storing blueprints and other blueprint folders. +struct IkarusBlueprintFolder { + /// \private \brief The id of the blueprint folder. + IkarusId id; +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/folders/entity_folder.h b/include/ikarus/folders/entity_folder.h new file mode 100644 index 0000000..2bffa3f --- /dev/null +++ b/include/ikarus/folders/entity_folder.h @@ -0,0 +1,22 @@ +#pragma once + +/// \file entity_folder.h +/// \author Folling + +#include +#include + +/// \addtogroup folder Folders +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A entity folder, storing entities and other entity folders. +struct IkarusEntityFolder { + /// \private \brief The id of the entity folder. + IkarusId id; +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/folders/folder.h b/include/ikarus/folders/folder.h new file mode 100644 index 0000000..0cdf946 --- /dev/null +++ b/include/ikarus/folders/folder.h @@ -0,0 +1,76 @@ +#pragma once + +/// \file folder.h +/// \author Folling + +#include +#include +#include +#include +#include + +IKARUS_BEGIN_HEADER + +/// \defgroup folder Folders +/// \brief Folders are used to group objects together. +/// @{ + +/// \private \brief The data of a folder. +union IkarusFolderData { + /// \private \brief The blueprint folder data of the folder. + IkarusBlueprintFolder blueprint_folder; + /// \private \brief The property folder data of the folder. + IkarusPropertyFolder property_folder; + /// \private \brief The entity folder data of the folder. + IkarusEntityFolder entity_folder; +}; + +/// \brief A generic folder. Similar to how Objects wrap all types of objects, Folders wrap all types of folders. +struct IkarusFolder { + /// \private \brief The data of the folder. + IkarusFolderData data; + + /// \private \brief The type of the folder. + IkarusFolderType type; +}; + +/// \brief Constructs a folder from a blueprint folder. +/// \param blueprint_folder The blueprint folder to construct the folder from. +/// \return The constructed folder. +IKA_API IkarusFolder ikarus_folder_from_blueprint_folder(IkarusBlueprintFolder blueprint_folder); +/// \brief Constructs a folder from a property folder. +/// \param property_folder The property folder to construct the folder from. +/// \return The constructed folder. +IKA_API IkarusFolder ikarus_folder_from_property_folder(IkarusPropertyFolder property_folder); +/// \brief Constructs a folder from an entity folder. +/// \param entity_folder The entity folder to construct the folder from. +/// \return The constructed folder. +IKA_API IkarusFolder ikarus_folder_from_entity_folder(IkarusEntityFolder entity_folder); + +/// \brief Fetches the folder type of a folder. +/// \param folder The folder to fetch the type of. +/// \return The type of the folder. +IKA_API IkarusFolderType ikarus_folder_get_type(IkarusFolder folder); + +/// \brief Checks if two folders are equal. +/// \details Since ids store the type of the object, this boils down to a simple comparison of the ids. +/// \param left The left side of the comparison. +/// \param right The right side of the comparison. +/// \return True if the folders are equal, false otherwise +IKA_API bool ikarus_folder_is_equal(IkarusFolder left, IkarusFolder right); + +/// \brief Visits a folder. Calling the appropriate function for the folder's type. +/// \param folder The folder to visit. +/// \param blueprint The function to call if the folder is a blueprint folder. +/// \param property The function to call if the folder is a property folder. +/// \param entity The function to call if the folder is an entity folder. +/// \param data The data to pass to the functions. +IKA_API void ikarus_folder_visit( + IkarusFolder folder, + void (*blueprint)(IkarusBlueprintFolder, void *), + void (*property)(IkarusPropertyFolder, void *), + void (*entity)(IkarusEntityFolder, void *), + void * data +); + +IKARUS_END_HEADER diff --git a/include/ikarus/folders/folder_type.h b/include/ikarus/folders/folder_type.h new file mode 100644 index 0000000..2a63e46 --- /dev/null +++ b/include/ikarus/folders/folder_type.h @@ -0,0 +1,26 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_FOLDER_TYPES + +/// \file folder_type.h +/// \author Folling + +#include + +/// \addtogroup folder folders +/// @{ + +/// \brief The type of an folder. +/// \remark Folders have the 8th bit set. +enum IkarusFolderType { + /// \brief Not a folder or no folder. + IkarusFolderType_None = 0, + /// \brief An IkarusBlueprintFolder + IkarusFolderType_BlueprintFolder = 0b1000'0001, + /// \brief An IkarusPropertyFolder + IkarusFolderType_PropertyFolder = 0b1000'0010, + /// \brief An IkarusEntityFolder + IkarusFolderType_EntityFolder = 0b1000'0011, +}; + +/// @} diff --git a/include/ikarus/folders/property_folder.h b/include/ikarus/folders/property_folder.h new file mode 100644 index 0000000..8e6709f --- /dev/null +++ b/include/ikarus/folders/property_folder.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +/// \file property_folder.h +/// \author Folling + +/// \addtogroup folder Folders +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A property folder, storing properties and other property folders. +/// \remark Property folders are scoped to the blueprint or entity they are associated with. +struct IkarusPropertyFolder { + /// \private \brief The id of the property folder. + IkarusId id; +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/types/id.h b/include/ikarus/id.h similarity index 97% rename from include/ikarus/types/id.h rename to include/ikarus/id.h index e2ca0b5..d184e63 100644 --- a/include/ikarus/types/id.h +++ b/include/ikarus/id.h @@ -6,12 +6,11 @@ #pragma once #include +#include #include IKARUS_BEGIN_HEADER -#include - /// \defgroup id Ids /// \brief Ids are used to identify objects in the database. /// \details They are stored as 64 bit integers with the following layout: @@ -64,4 +63,4 @@ IKA_API bool ikarus_id_is_unspecified(IkarusId id); /// @} -IKARUS_END_HEADER \ No newline at end of file +IKARUS_END_HEADER diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h new file mode 100644 index 0000000..bd0abd3 --- /dev/null +++ b/include/ikarus/objects/blueprint.h @@ -0,0 +1,24 @@ +#pragma once + +/// \file blueprint.h +/// \author Folling + +#include +#include + +/// \defgroup blueprints Blueprints +/// \brief Blueprints are templates for entities. + +IKARUS_BEGIN_HEADER + +/// \brief A blueprint object. +/// \details A blueprint is a collection of properties which can be linked to entities. +/// Each entity the blueprint is linked to will have values for the blueprints properties. +struct IkarusBlueprint { + /// \private \brief The id of the blueprint. + IkarusId id; +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h new file mode 100644 index 0000000..980c03e --- /dev/null +++ b/include/ikarus/objects/entity.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +/// \file entity.h +/// \author Folling + +/// \defgroup entities Entities +/// \brief Entities are the core building blocks of Ikarus. + +IKARUS_BEGIN_HEADER + +/// \brief Entities are the core building blocks of Ikarus. +/// \detials Blueprints and Properties define the structure of the data. +/// Entities define the data itself. +/// +/// Properties can be associated with Entities in two ways: +/// - Directly: The property is linked to the entity. +/// - Indirectly: The property is linked to a blueprint of the entity. +/// +/// For each property an entity is linked to, it has a value. These values depend on the property's type. +/// For more information on the types see the property documentation. +/// +/// Values are the core type of data within Ikarus. +/// Each value is associated with one page and one property. +/// +/// \remark Values are typed, the type of a value is specified by its associated property. +/// For more information on the types see the property documentation. +/// +/// \remark Values are guaranteed to be in valid format for a given type +/// but not guaranteed to be valid under the settings of the property. +/// This is because changing the settings can invalidate existing values without resetting them. +struct IkarusEntity { + /// \private \brief The ID of the entity. + IkarusId id; +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h new file mode 100644 index 0000000..ef8c592 --- /dev/null +++ b/include/ikarus/objects/object.h @@ -0,0 +1,123 @@ +#pragma once + +/// \file object.h +/// \author Folling + +#include +#include +#include +#include +#include +#include +#include +#include + +/// \defgroup object Objects +/// \brief Objects are a compound type of all types of objects in the database. +/// \details The following objects currently exist: +/// - blueprints +/// - properties +/// - entities +/// - blueprint folders +/// - property folders +/// - entity folders +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusFolder; + +/// \private \brief The data of an object. +union IkarusObjectData { + /// \private \brief The blueprint data of the object. + IkarusBlueprint blueprint; + /// \private \brief The property data of the object. + IkarusProperty property; + /// \private \brief The entity data of the object. + IkarusEntity entity; + /// \private \brief The blueprint folder data of the object. + IkarusBlueprintFolder blueprint_folder; + /// \private \brief The property folder data of the object. + IkarusPropertyFolder property_folder; + /// \private \brief The entity folder data of the object. + IkarusEntityFolder entity_folder; +}; + +/// \brief A generic object. Wraps all types of objects, including folders. +struct IkarusObject { + /// \private \brief The type of the object. + IkarusObjectType type; + /// \private \brief The data of the object. + IkarusObjectData data; +}; + +/// \brief Constructs an object from a blueprint. +/// \param blueprint The blueprint to construct the object from. +/// \return The constructed object, representing the blueprint. +IKA_API IkarusObject ikarus_object_from_blueprint(IkarusBlueprint blueprint); + +/// \brief Constructs an object from a property. +/// \param property The property to construct the object from. +/// \return The constructed object, representing the property. +IKA_API IkarusObject ikarus_object_from_property(IkarusProperty property); + +/// \brief Constructs an object from an entity. +/// \param entity The entity to construct the object from. +/// \return The constructed object, representing the entity. +IKA_API IkarusObject ikarus_object_from_entity(IkarusEntity entity); + +/// \brief Constructs an object from a blueprint folder. +/// \param blueprint The folder to construct the object from. +/// \return The constructed object, representing the folder. +IKA_API IkarusObject ikarus_object_from_blueprint_folder(IkarusBlueprintFolder folder); + +/// \brief Constructs an object from a property folder. +/// \param property The folder to construct the object from. +/// \return The constructed object, representing the folder. +IKA_API IkarusObject ikarus_object_from_property_folder(IkarusPropertyFolder folder); + +/// \brief Constructs an object from a entity folder. +/// \param entity The folder to construct the object from. +/// \return The constructed object, representing the folder. +IKA_API IkarusObject ikarus_object_from_entity_folder(IkarusEntityFolder folder); + +/// \brief Constructs an object from a folder. +/// \param folder The folder to construct the object from. +/// \return The constructed object, representing the folder. +IKA_API IkarusObject ikarus_object_from_folder(IkarusFolder folder); + +/// \brief Compares two objects for equality. +/// \details Since ids store the type of the object, this boils down to a simple comparison of the ids. +/// \param left The left side of the comparison. +/// \param right The right side of the comparison. +/// \return Whether the objects are equal. +IKA_API bool ikarus_object_is_equal(IkarusObject left, IkarusObject right); + +/// \brief Fetches the type of an object. +/// \param object The object to fetch the type of. +/// \return The type of the object. +IKA_API IkarusObjectType ikarus_object_get_type(IkarusObject object); + +/// \brief Visits an object. Calling the appropriate function for the object's type. +/// \param object The object to visit. +/// \param blueprint The function to call if the object is a blueprint. +/// \param property The function to call if the object is a property. +/// \param entity The function to call if the object is an entity. +/// \param blueprint_folder The function to call if the object is a blueprint folder. +/// \param property_folder The function to call if the object is a property folder. +/// \param entity_folder The function to call if the object is an entity folder. +/// \param data The data to pass to the functions. +IKA_API void ikarus_object_visit( + IkarusObject object, + void (*visit_blueprint)(IkarusBlueprint, void *), + void (*visit_property)(IkarusProperty, void *), + void (*visit_entity)(IkarusEntity, void *), + void (*visit_blueprint_folder)(IkarusBlueprintFolder, void *), + void (*visit_property_folder)(IkarusPropertyFolder, void *), + void (*visit_entity_folder)(IkarusEntityFolder, void *), + void * data +); + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h new file mode 100644 index 0000000..0fafc68 --- /dev/null +++ b/include/ikarus/objects/object_type.h @@ -0,0 +1,38 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_OBJECT_TYPES + +/// \file object.h +/// \author Folling + +#include +#include + +/// \addtogroup object Objects +/// @{ + +/// \brief The type of an object. +/// \remark Folders have the 4th bit set. +enum IkarusObjectType { + /// \brief Not an object or no object. + IkarusObjectType_None = 0, + /// \brief An IkarusBlueprint. + IkarusObjectType_Blueprint = 1, + /// \brief An IkarusProperty. + IkarusObjectType_Property = 2, + /// \brief An IkarusEntity. + IkarusObjectType_Entity = 3, + /// \brief An IkarusBlueprintFolder + IkarusObjectType_BlueprintFolder = IkarusFolderType_BlueprintFolder, + /// \brief An IkarusPropertyFolder + IkarusObjectType_PropertyFolder = IkarusFolderType_PropertyFolder, + /// \brief An IkarusEntityFolder + IkarusObjectType_EntityFolder = IkarusFolderType_EntityFolder, +}; + +/// \brief Constructs an IkarusObjectType from an IkarusFolderType. +/// \param type The IkarusFolderType of which to construct the IkarusObjectType from. +/// \return The constructed IkarusObjectType, representing the folder type. +IKA_API IkarusObjectType ikarus_object_type_from_folder_type(IkarusFolderType type); + +/// @} diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h new file mode 100644 index 0000000..d104ac3 --- /dev/null +++ b/include/ikarus/objects/property.h @@ -0,0 +1,73 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION, IMPLEMENTATION_DETAIL_PROPERTY_TYPES + +/// \file property.h +/// \author Folling + +#include +#include +#include +#include + +/// \defgroup properties Properties +/// \brief Properties define the structure and types of data. + +IKARUS_BEGIN_HEADER + +/// \brief Properties are the placeholders of values for entities. +/// \details Each entity can have any number of properties. +/// Every property has a type that identifies the kind of data that can be put in. +/// +/// 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) +/// +/// Every property has settings which can be used to customise the property further. +/// Two settings that are shared among all properties are the following: +/// - Multiple +/// - Allow undefined +/// +/// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. +/// The latter allows you to specify an "unknown" value for a property. +/// It might not be known if a character is dead or not for example. +/// +/// Each entity associated with the property has a value for it. +/// +/// Properties can also be added to blueprints in which case they are available for all entities associated with the +/// blueprint. +/// +/// We call properties within entities "Entity Properties" and properties within blueprints "Blueprint Properties". +/// +/// \remark Properties are scoped to the blueprint or entity they are associated with. +/// \remark Values for properties are lazily created as space saving measure. +/// Fetching the value for some property of some entity will return the property's default value if none is specified. +/// This default value is specified when the property is created and can be updated later. +/// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. +struct IkarusProperty { + /// \private \brief The id of the property. + IkarusId id; +}; + +/// \brief The type of a property. +/// \details Designates the type of data stored by the property as well as which settings are +/// available. +/// \see IkarusPropertySettings +enum IkarusPropertyType { + /// \brief A true/false boolean-like value. + IkarusPropertyType_Toggle, + /// \brief An arbitrary numeric value. + IkarusPropertyType_Number, + /// \brief An arbitrary textual value. + IkarusPropertyType_Text, +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/types/property_type.h b/include/ikarus/objects/property_type.h similarity index 96% rename from include/ikarus/types/property_type.h rename to include/ikarus/objects/property_type.h index d22e1a6..3709a37 100644 --- a/include/ikarus/types/property_type.h +++ b/include/ikarus/objects/property_type.h @@ -2,7 +2,7 @@ // IMPLEMENTATION_DETAIL_PROPERTY_TYPES -/// \file id.h +/// \file property_type.h /// \author Folling #include diff --git a/include/ikarus/scopes/blueprint_scope.h b/include/ikarus/scopes/blueprint_scope.h new file mode 100644 index 0000000..eac5eac --- /dev/null +++ b/include/ikarus/scopes/blueprint_scope.h @@ -0,0 +1,26 @@ +#pragma once + +/// \file blueprint_scope.h +/// \author Folling + +#include +#include + +/// \addtogroup object_scopes ObjectScopes +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief The global scope of all blueprints. +struct IkarusBlueprintScope { + /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. + short _dummy; +}; + +/// \brief Creates a blueprint scope. +/// \return The created blueprint scope. +IKA_API IkarusBlueprintScope ikarus_blueprint_scope_create(); + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/scopes/entity_scope.h b/include/ikarus/scopes/entity_scope.h new file mode 100644 index 0000000..e29d725 --- /dev/null +++ b/include/ikarus/scopes/entity_scope.h @@ -0,0 +1,26 @@ +#pragma once + +/// \file entity_scope.h +/// \author Folling + +#include +#include + +/// \addtogroup object_scopes ObjectScopes +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief The global scope of all entities. +struct IkarusEntityScope { + /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. + short _dummy; +}; + +/// \brief Creates a entity scope. +/// \return The created entity scope. +IKA_API IkarusEntityScope ikarus_entity_scope_create(); + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/scopes/object_scope.h b/include/ikarus/scopes/object_scope.h new file mode 100644 index 0000000..1c16d9f --- /dev/null +++ b/include/ikarus/scopes/object_scope.h @@ -0,0 +1,84 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_OBJECT_SCOPES + +/// \file object_scope.h +/// \author Folling + +#include +#include +#include +#include + +/// \defgroup object_scopes Object Scopes +/// \brief Scopes define where objects belong to. +/// \details They are required to differentiate between different types of objects with NULL as their parent. +/// @{ + +IKARUS_BEGIN_HEADER + +/// \private \brief The data for an object scope. +union IkarusObjectScopeData { + /// \private \brief The blueprint data of the scope. + IkarusBlueprintScope _blueprint; + /// \private \brief The property data of the scope. + IkarusPropertyScope _property; + /// \private \brief The entity data of the scope. + IkarusEntityScope _entity; +}; + +/// The type of an object scope. +enum IkarusObjectScopeType { + /// \brief The scope is a blueprint scope. + IkarusObjectScopeType_Blueprint, + /// \brief The scope is a property scope. + IkarusObjectScopeType_Property, + /// \brief The scope is an entity scope. + IkarusObjectScopeType_Entity +}; + +/// \brief The scope of an object. +struct IkarusObjectScope { + /// \private \brief Represents the type of the scope. + IkarusObjectScopeType _type; + /// \private \brief Represents the data of the scope. + IkarusObjectScopeData _data; +}; + +/// \brief Converts a blueprint scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope scope); + +/// \brief Converts a property scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope scope); + +/// Converts an entity scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope scope); + +/// \brief Fetches the type of an object scope. +/// \param scope The scope to fetch the type of. +/// \return The type of the scope. +IKA_API IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope scope); + +/// \brief Visits an object scope, calling the appropriate function. +/// \param scope The scope to visit. +/// \param blueprint The function to call if the scope is an #IkarusBlueprintScope. +/// \param property The function to call if the scope is an #IkarusPropertyScope. +/// \param entity The function to call if the scope is an #IkarusEntityScope. +/// \remark function pointers may be null in which case they are not called. +IKA_API void ikarus_object_scope_visit( + IkarusObjectScope scope, + void (*blueprint)(IkarusBlueprintScope, void *), + void (*property)(IkarusPropertyScope, void *), + void (*entity)(IkarusEntityScope, void *), + void * data +); + +/// @} + +IKARUS_END_HEADER diff --git a/include/ikarus/scopes/property_scope.h b/include/ikarus/scopes/property_scope.h new file mode 100644 index 0000000..6de365c --- /dev/null +++ b/include/ikarus/scopes/property_scope.h @@ -0,0 +1,65 @@ +#pragma once + +/// \file property_scope.h +/// \author Folling + +#include +#include +#include +#include + +/// \addtogroup object_scopes ObjectScopes +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief Data for a property scope. This can either be a blueprint or an entity. +union IkarusPropertyScopeData { + /// \private \brief The blueprint the property is scoped to. + IkarusBlueprint _blueprint; + /// \private \brief The entity the property is scoped to. + IkarusEntity _entity; +}; + +/// \brief The type of a property scope. This can either be a blueprint or an entity. +enum IkarusPropertyScopeType { + /// \brief The property is scoped to a blueprint. + IkarusPropertyScopeType_Blueprint, + /// \brief The property is scoped to an entity. + IkarusPropertyScopeType_Entity +}; + +/// \brief The scope of a property +struct IkarusPropertyScope { + /// \private \brief Represents the type of the scope. + IkarusPropertyScopeType _type; + /// \private \brief Represents the data of the scope. + IkarusPropertyScopeData _data; +}; + +/// \brief Creates a property scope from a blueprint. +/// \param blueprint The blueprint the property is scoped to. +/// \return The created property scope. +IKA_API IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint blueprint); +/// \brief Creates a property scope from a entity. +/// \param entity The entity the property is scoped to. +/// \return The created property scope. +IKA_API IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity entity); + +/// \brief Fetches the type of an property scope. +/// \param scope The scope to fetch the type of. +/// \return The type of the scope. +IKA_API IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope scope); + +/// \brief Visits a property scope, calling the appropriate function. +/// \param scope The scope to to visit +/// \param blueprint The function to call if the property is scoped to a blueprint. +/// \param entity The function to call if the property is scoped to an entity. +/// \param data Optional data to pass to the functions. +void ikarus_property_scope_visit( + IkarusPropertyScope scope, void (*blueprint)(IkarusBlueprint, void *), void (*entity)(IkarusEntity, void *), void * data +); + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/types/object.h b/include/ikarus/types/object.h deleted file mode 100644 index 9d8b9bd..0000000 --- a/include/ikarus/types/object.h +++ /dev/null @@ -1,164 +0,0 @@ -#pragma once - -// IMPLEMENTATION_DETAIL_OBJECT_TYPES -// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION -// IMPLEMENTATION_DETAIL_PROPERTY_TYPES - -/// \file id.h -/// \author Folling - -/// \defgroup object Objects -/// \brief Objects are a compound type of all types of objects in the database. -/// \details The following objects currently exist: -/// - blueprints -/// - properties -/// - entities -/// - blueprint folders -/// - property folders -/// - entity folders -/// @{ - -#include - -IKARUS_BEGIN_HEADER - -/// \brief A blueprint object. -/// \details A blueprint is a collection of properties which can be linked to entities. -/// Each entity the blueprint is linked to will have values for the blueprints properties. -struct IkarusBlueprint { - IkarusId id; -}; - -/// \brief Properties are the placeholders of values for entities. -/// \details Each entity can have any number of properties. -/// Every property has a type that identifies the kind of data that can be put in. -/// -/// 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) -/// -/// Every property has settings which can be used to customise the property further. -/// Two settings that are shared among all properties are the following: -/// - Multiple -/// - Allow undefined -/// -/// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. -/// The latter allows you to specify an "unknown" value for a property. -/// It might not be known if a character is dead or not for example. -/// -/// Each entity associated with the property has a value for it. -/// -/// Properties can also be added to blueprints in which case they are available for all entities associated with the -/// blueprint. -/// -/// We call properties within entities "Entity Properties" and properties within blueprints "Blueprint Properties". -/// -/// \remark Values for properties are lazily created as space saving measure. -/// Fetching the value for some property of some entity will return the property's default value if none is specified. -/// This default value is specified when the property is created and can be updated later. -/// -/// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. -struct IkarusProperty { - /// \private \brief The ID of the property. - IkarusId id; -}; - -/// \brief Entities are the core building blocks of Ikarus. -/// \detials Blueprints and Properties define the structure of the data. -/// Entities define the data itself. -/// -/// Properties can be associated with Entities in two ways: -/// - Directly: The property is linked to the entity. -/// - Indirectly: The property is linked to a blueprint of the entity. -/// -/// For each property an entity is linked to, it has a value. These values depend on the property's type. -/// For more information on the types see the property documentation. -/// -/// Values are the core type of data within Ikarus. -/// Each value is associated with one page and one property. -/// -/// \remark Values are typed, the type of a value is specified by its associated property. -/// For more information on the types see the property documentation. -/// -/// \remark Values are guaranteed to be in valid format for a given type -/// but not guaranteed to be valid under the settings of the property. -/// This is because changing the settings can invalidate existing values without resetting them. -struct IkarusEntity { - /// \private \brief The ID of the entity. - IkarusId id; -}; - -/// \brief A blueprint folder. -/// \see Folder -struct IkarusBlueprintFolder { - /// \private \brief The ID of the folder. - IkarusId id; -}; - -/// \brief A property folder. -/// \remark Property folders are scoped to the blueprint or entity they are associated with. -/// \see Folder -struct IkarusPropertyFolder { - /// \private \brief The ID of the folder. - IkarusId id; -}; - -/// \brief An entity folder. -/// \see Folder -struct IkarusEntityFolder { - /// \private \brief The ID of the folder. - IkarusId id; -}; - -/// \private \brief The data of a folder. -union IkarusFolderData { - /// \private \brief The blueprint folder data of the folder. - IkarusBlueprintFolder blueprint_folder; - /// \private \brief The property folder data of the folder. - IkarusPropertyFolder property_folder; - /// \private \brief The entity folder data of the folder. - IkarusEntityFolder entity_folder; -}; - -/// \brief A generic folder. Similar to how Objects wrap all types of objects, Folders wrap all types of folders. -struct IkarusFolder { - /// \private \brief The data of the folder. - IkarusFolderData data; - - /// \private \brief The type of the folder. - IkarusFolderType type; -}; - -/// \private \brief The data of an object. -union IkarusObjectData { - /// \private \brief The blueprint data of the object. - IkarusBlueprint blueprint; - /// \private \brief The property data of the object. - IkarusProperty property; - /// \private \brief The entity data of the object. - IkarusEntity entity; - /// \private \brief The blueprint folder data of the object. - IkarusBlueprintFolder blueprint_folder; - /// \private \brief The property folder data of the object. - IkarusPropertyFolder property_folder; - /// \private \brief The entity folder data of the object. - IkarusEntityFolder entity_folder; -}; - -/// \brief A generic object. Wraps all types of objects, including folders. -struct IkarusObject { - /// \private \brief The data of the object. - IkarusObjectData data; - /// \private \brief The type of the object. - IkarusObjectType type; -}; - -// @} - -IKARUS_END_HEADER diff --git a/include/ikarus/types/object_scope.h b/include/ikarus/types/object_scope.h deleted file mode 100644 index 1fe51e1..0000000 --- a/include/ikarus/types/object_scope.h +++ /dev/null @@ -1,149 +0,0 @@ -// IMPLEMENTATION_DETAIL_OBJECT_SCOPES, IMPLEMENTATION_DETAIL_TREE_LAYOUT - -/// \file object_scope.h -/// \author Folling - -/// \defgroup object_scopes Object Scopes -/// \brief Scopes define where objects belong to. -/// \details They are required to differentiate between different types of objects with NULL as their parent. -/// @{ - -#pragma once - -#include -#include - -IKARUS_BEGIN_HEADER - -/// \brief The global scope of all blueprints. -struct IkarusBlueprintScope { - /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. - short _dummy; -}; - -/// \brief Data for a property scope. This can either be a blueprint or an entity. -union IkarusPropertyScopeData { - /// \private \brief The blueprint the property is scoped to. - IkarusBlueprint _blueprint; - /// \private \brief The entity the property is scoped to. - IkarusEntity _entity; -}; - -/// \brief The type of a property scope. This can either be a blueprint or an entity. -enum IkarusPropertyScopeType { - /// \brief The property is scoped to a blueprint. - IkarusPropertyScopeType_Blueprint, - /// \brief The property is scoped to an entity. - IkarusPropertyScopeType_Entity -}; - -/// \brief The scope of a property -struct IkarusPropertyScope { - /// \private \brief Represents the type of the scope. - IkarusPropertyScopeType _type; - /// \private \brief Represents the data of the scope. - IkarusPropertyScopeData _data; -}; - -/// The global scope of all entities. -struct IkarusEntityScope { - /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. - short _dummy; -}; - -/// \private \brief The data for an object scope. -union IkarusObjectScopeData { - /// \private \brief The blueprint data of the scope. - IkarusBlueprintScope _blueprint; - /// \private \brief The property data of the scope. - IkarusPropertyScope _property; - /// \private \brief The entity data of the scope. - IkarusEntityScope _entity; -}; - -/// The type of an object scope. -enum IkarusObjectScopeType { - /// \brief The scope is a blueprint scope. - IkarusObjectScopeType_Blueprint, - /// \brief The scope is a property scope. - IkarusObjectScopeType_Property, - /// \brief The scope is an entity scope. - IkarusObjectScopeType_Entity -}; - -/// \brief The scope of an object. -struct IkarusObjectScope { - /// \private \brief Represents the type of the scope. - IkarusObjectScopeType _type; - /// \private \brief Represents the data of the scope. - IkarusObjectScopeData _data; -}; - -/// \brief Creates a blueprint scope. -/// \return The created blueprint scope. -IKA_API IkarusBlueprintScope ikarus_blueprint_scope_create(); -/// \brief Converts a blueprint scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope); - -/// \brief Creates a property scope from a blueprint. -/// \param blueprint The blueprint the property is scoped to. -/// \return The created property scope. -IKA_API IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint const * blueprint); -/// \brief Creates a property scope from a entity. -/// \param entity The entity the property is scoped to. -/// \return The created property scope. -IKA_API IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity const * entity); -/// \brief Converts a property scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope); - -/// \brief Fetches the type of an property scope. -/// \param scope The scope to fetch the type of. -/// \return The type of the scope. -IKA_API IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope const * scope); - -/// \brief Visits a property scope, calling the appropriate function. -/// \param scope The scope to to visit -/// \param blueprint The function to call if the property is scoped to a blueprint. -/// \param entity The function to call if the property is scoped to an entity. -/// \param data Optional data to pass to the functions. -void ikarus_property_scope_visit( - IkarusPropertyScope const * scope, - void (*blueprint)(IkarusBlueprint const *, void *), - void (*entity)(IkarusEntity const *, void *), - void * data -); - -/// \brief Creates an entity scope. -/// \return The created entity scope. -IKA_API IkarusEntityScope ikarus_entity_scope_create(); -/// Converts an entity scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope); - -/// \brief Fetches the type of an object scope. -/// \param scope The scope to fetch the type of. -/// \return The type of the scope. -IKA_API IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope const * scope); - -/// \brief Visits an object scope, calling the appropriate function. -/// \param scope The scope to visit. -/// \param blueprint The function to call if the scope is an #IkarusBlueprintScope. -/// \param property The function to call if the scope is an #IkarusPropertyScope. -/// \param entity The function to call if the scope is an #IkarusEntityScope. -/// \remark function pointers may be null in which case they are not called. -IKA_API void ikarus_object_scope_visit( - IkarusObjectScope const * scope, - void (*blueprint)(IkarusBlueprintScope const *, void *), - void (*property)(IkarusPropertyScope const *, void *), - void (*entity)(IkarusEntityScope const *, void *), - void * data -); - -/// @} - -IKARUS_END_HEADER diff --git a/include/ikarus/types/object_type.h b/include/ikarus/types/object_type.h deleted file mode 100644 index fd8685a..0000000 --- a/include/ikarus/types/object_type.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include - -IKARUS_BEGIN_HEADER - -/// \defgroup object_types ObjectTypes -/// \brief ObjectTypes are used to identify the type of objects. -/// @{ - -/// \brief The type of a folder. -/// \remark These values are identical to the associated values of IkarusObjectType. -enum IkarusFolderType { - /// \brief Not a folder or no folder. - IkarusFolderType_None = 0, - /// \brief An IkarusBlueprintFolder - IkarusFolderType_BlueprintFolder = 17, - /// \brief An IkarusPropertyFolder - IkarusFolderType_PropertyFolder = 18, - /// \brief An IkarusEntityFolder - IkarusFolderType_EntityFolder = 19, -}; - -/// \brief The type of an object. -/// \remark Folders have the 4th bit set. -enum IkarusObjectType { - /// \brief Not an object or no object. - IkarusObjectType_None = 0, - /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 1, - /// \brief An IkarusProperty. - IkarusObjectType_Property = 2, - /// \brief An IkarusEntity. - IkarusObjectType_Entity = 3, - /// \brief An IkarusBlueprintFolder - IkarusObjectType_BlueprintFolder = IkarusFolderType_BlueprintFolder, - /// \brief An IkarusPropertyFolder - IkarusObjectType_PropertyFolder = IkarusFolderType_PropertyFolder, - /// \brief An IkarusEntityFolder - IkarusObjectType_EntityFolder = IkarusFolderType_EntityFolder, -}; - -// because of the nature of bitsets, the largest possible object-type is 31 -/// \brief A bitset of IkarusObjectType%s. -enum IkarusObjectTypes { - /// \brief No object type. - IkarusObjectTypes_None = 0, - /// \brief An IkarusBlueprint. - IkarusObjectTypes_Blueprint = 1 << IkarusObjectType_Blueprint, - /// \brief An IkarusProperty. - IkarusObjectTypes_Property = 1 << IkarusObjectType_Property, - /// \brief An IkarusEntity. - IkarusObjectTypes_Entity = 1 << IkarusObjectType_Entity, - /// \brief An IkarusBlueprintFolder - IkarusObjectTypes_BlueprintFolder = 1 << IkarusObjectType_BlueprintFolder, - /// \brief An IkarusPropertyFolder - IkarusObjectTypes_PropertyFolder = 1 << IkarusObjectType_PropertyFolder, - /// \brief An IkarusEntityFolder - IkarusObjectTypes_EntityFolder = 1 << IkarusObjectType_EntityFolder, -}; - -/// \brief Converts an IkarusFolderType to an IkarusObjectType. -/// \param type The IkarusFolderType to convert. -/// \return The converted IkarusObjectType, representing the folder type. -IKA_API IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type); -/// \brief Converts an IkarusObjectType to a bitset of IkarusObjectTypes. -/// \param type The IkarusObjectType to convert. -/// \return The converted IkarusObjectTypes, representing the object type. -IKA_API IkarusObjectTypes ikarus_object_type_to_bitset(IkarusObjectType type); - -// @} - -IKARUS_END_HEADER diff --git a/include/ikarus/types/value.h b/include/ikarus/values/value.h similarity index 98% rename from include/ikarus/types/value.h rename to include/ikarus/values/value.h index d636d27..e1457af 100644 --- a/include/ikarus/types/value.h +++ b/include/ikarus/values/value.h @@ -2,9 +2,12 @@ // IMPLEMENTATION_DETAIL_PROPERTY_TYPES +/// \file value.h +/// \author Folling + #include +#include #include -#include IKARUS_BEGIN_HEADER diff --git a/src/types/id.cpp b/src/id.cpp similarity index 93% rename from src/types/id.cpp rename to src/id.cpp index 53f2689..caa08be 100644 --- a/src/types/id.cpp +++ b/src/id.cpp @@ -1,11 +1,11 @@ -#include "ikarus/types/id.h" +#include "ikarus/id.h" #include +#include + IkarusId ikarus_id_from_data(int64_t data) { - return IkarusId { - .data = data - }; + return IkarusId{.data = data}; } IkarusObjectType ikarus_id_get_object_type(IkarusId id) { diff --git a/src/objects/object.cpp b/src/objects/object.cpp new file mode 100644 index 0000000..7378c39 --- /dev/null +++ b/src/objects/object.cpp @@ -0,0 +1,27 @@ +#include "ikarus/objects/object.h" + +#include + +#include + +#include + +IkarusObjectType ikarus_object_get_type(IkarusObject object) { + return object.type; +} + +TEST_CASE("object_type", "[object]") { + auto types = { + IkarusObjectType_Blueprint, + IkarusObjectType_Property, + IkarusObjectType_Entity, + IkarusObjectType_BlueprintFolder, + IkarusObjectType_PropertyFolder, + IkarusObjectType_EntityFolder, + }; + + for (auto type : types) { + auto object = IkarusObject{.type = type}; + REQUIRE(ikarus_object_get_type(object) == type); + } +} diff --git a/src/objects/object_type.cpp b/src/objects/object_type.cpp new file mode 100644 index 0000000..a9a3e32 --- /dev/null +++ b/src/objects/object_type.cpp @@ -0,0 +1,16 @@ +#include "ikarus/objects/object_type.h" + +#include + +#include + +IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type) { + return static_cast(type); +} + +TEST_CASE("folder_to_object_type_conversion", "[object_type]") { + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_None) == IkarusObjectType_None); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_BlueprintFolder) == IkarusObjectType_BlueprintFolder); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_PropertyFolder) == IkarusObjectType_PropertyFolder); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_EntityFolder) == IkarusObjectType_EntityFolder); +} diff --git a/src/types/object_scope.cpp b/src/scopes/object_scope.cpp similarity index 62% rename from src/types/object_scope.cpp rename to src/scopes/object_scope.cpp index a4f2b49..348b007 100644 --- a/src/types/object_scope.cpp +++ b/src/scopes/object_scope.cpp @@ -1,52 +1,56 @@ -#include "ikarus/types/object_scope.h" +#include "ikarus/scopes/object_scope.h" +#include #include #include +#include +#include +#include +#include +#include + IkarusBlueprintScope ikarus_blueprint_scope_create() { return IkarusBlueprintScope{._dummy = 0}; } -IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope) { +IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope scope) { IkarusObjectScopeData data{}; - data._blueprint = *scope; + data._blueprint = scope; return IkarusObjectScope{._type = IkarusObjectScopeType_Blueprint, ._data = data}; } -IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint const * blueprint) { +IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint blueprint) { IkarusPropertyScopeData data{}; - data._blueprint = *blueprint; + data._blueprint = blueprint; return IkarusPropertyScope{._type = IkarusPropertyScopeType_Blueprint, ._data = data}; } -IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity const * entity) { +IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity entity) { IkarusPropertyScopeData data{}; - data._entity = *entity; + data._entity = entity; return IkarusPropertyScope{._type = IkarusPropertyScopeType_Entity, ._data = data}; } -IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope) { +IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope scope) { IkarusObjectScopeData data{}; - data._property = *scope; + data._property = scope; return IkarusObjectScope{._type = IkarusObjectScopeType_Property, ._data = data}; } -IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope const * scope) { - return scope->_type; +IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope scope) { + return scope._type; } void ikarus_property_scope_visit( - IkarusPropertyScope const * scope, - void (*blueprint)(IkarusBlueprint const *, void *), - void (*entity)(IkarusEntity const *, void *), - void * data + IkarusPropertyScope scope, void(blueprint)(IkarusBlueprint, void *), void(entity)(IkarusEntity, void *), void * data ) { - switch (scope->_type) { - case IkarusPropertyScopeType_Blueprint: blueprint(&scope->_data._blueprint, data); break; - case IkarusPropertyScopeType_Entity: entity(&scope->_data._entity, data); break; + switch (scope._type) { + case IkarusPropertyScopeType_Blueprint: blueprint(scope._data._blueprint, data); break; + case IkarusPropertyScopeType_Entity: entity(scope._data._entity, data); break; } } @@ -54,40 +58,40 @@ IkarusEntityScope ikarus_entity_scope_create() { return IkarusEntityScope{._dummy = 0}; } -IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope) { +IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope scope) { IkarusObjectScopeData data{}; - data._entity = *scope; + data._entity = scope; return IkarusObjectScope{._type = IkarusObjectScopeType_Entity, ._data = data}; } -IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope const * scope) { - return scope->_type; +IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope scope) { + return scope._type; } void ikarus_object_scope_visit( - IkarusObjectScope const * scope, - void (*blueprint)(IkarusBlueprintScope const *, void *), - void (*property)(IkarusPropertyScope const *, void *), - void (*entity)(IkarusEntityScope const *, void *), + IkarusObjectScope scope, + void(blueprint)(IkarusBlueprintScope, void *), + void(property)(IkarusPropertyScope, void *), + void(entity)(IkarusEntityScope, void *), void * data ) { - switch (scope->_type) { + switch (scope._type) { case IkarusObjectScopeType_Blueprint: { if (blueprint != nullptr) { - blueprint(&scope->_data._blueprint, data); + blueprint(scope._data._blueprint, data); } break; } case IkarusObjectScopeType_Property: { if (property != nullptr) { - property(&scope->_data._property, data); + property(scope._data._property, data); } break; } case IkarusObjectScopeType_Entity: { if (entity != nullptr) { - entity(&scope->_data._entity, data); + entity(scope._data._entity, data); } break; } @@ -96,7 +100,7 @@ void ikarus_object_scope_visit( TEST_CASE("blueprint_object_scope_conversion", "[object_scope]") { auto blueprint_scope = ikarus_blueprint_scope_create(); - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); REQUIRE(blueprint_object_scope._type == IkarusObjectScopeType_Blueprint); } @@ -104,24 +108,24 @@ TEST_CASE("property_scope_type", "[object_scope]") { auto blueprint = IkarusBlueprint{}; auto entity = IkarusEntity{}; - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); - auto property_entity_scope = ikarus_property_scope_create_entity(&entity); + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); + auto property_entity_scope = ikarus_property_scope_create_entity(entity); - REQUIRE(ikarus_property_scope_get_type(&property_blueprint_scope) == IkarusPropertyScopeType_Blueprint); - REQUIRE(ikarus_property_scope_get_type(&property_entity_scope) == IkarusPropertyScopeType_Entity); + REQUIRE(ikarus_property_scope_get_type(property_blueprint_scope) == IkarusPropertyScopeType_Blueprint); + REQUIRE(ikarus_property_scope_get_type(property_entity_scope) == IkarusPropertyScopeType_Entity); } TEST_CASE("property_object_scope_conversion", "[object_scope]") { auto blueprint = IkarusBlueprint{}; auto entity = IkarusEntity{}; - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); - auto property_blueprint_object_scope = ikarus_property_scope_to_object_scope(&property_blueprint_scope); + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); + auto property_blueprint_object_scope = ikarus_property_scope_to_object_scope(property_blueprint_scope); REQUIRE(property_blueprint_object_scope._type == IkarusObjectScopeType_Property); - auto property_entity_scope = ikarus_property_scope_create_entity(&entity); - auto property_entity_object_scope = ikarus_property_scope_to_object_scope(&property_entity_scope); + auto property_entity_scope = ikarus_property_scope_create_entity(entity); + auto property_entity_object_scope = ikarus_property_scope_to_object_scope(property_entity_scope); REQUIRE(property_entity_object_scope._type == IkarusObjectScopeType_Property); } @@ -130,24 +134,24 @@ TEST_CASE("property_scope_visiting", "[object_scope]") { auto blueprint = IkarusBlueprint{}; auto entity = IkarusEntity{}; - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); - auto property_entity_scope = ikarus_property_scope_create_entity(&entity); + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); + auto property_entity_scope = ikarus_property_scope_create_entity(entity); int test = 0; ikarus_property_scope_visit( - &property_blueprint_scope, - [](IkarusBlueprint const * _, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusEntity const * _, void * data) { *reinterpret_cast(data) = 2; }, + property_blueprint_scope, + [](IkarusBlueprint, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusEntity, void * data) { *reinterpret_cast(data) = 2; }, &test ); REQUIRE(test == 1); ikarus_property_scope_visit( - &property_entity_scope, - [](IkarusBlueprint const * _, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusEntity const * _, void * data) { *reinterpret_cast(data) = 2; }, + property_entity_scope, + [](IkarusBlueprint, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusEntity, void * data) { *reinterpret_cast(data) = 2; }, &test ); @@ -156,7 +160,7 @@ TEST_CASE("property_scope_visiting", "[object_scope]") { TEST_CASE("entity_object_scope_conversion", "[object_scope]") { auto entity_scope = ikarus_entity_scope_create(); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); REQUIRE(entity_object_scope._type == IkarusObjectScopeType_Entity); } @@ -164,28 +168,28 @@ TEST_CASE("object_scope_type_fetching", "[object_scope]") { auto blueprint = IkarusBlueprint{}; auto blueprint_scope = ikarus_blueprint_scope_create(); - auto property_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto property_scope = ikarus_property_scope_create_blueprint(blueprint); auto entity_scope = ikarus_entity_scope_create(); - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); - auto property_object_scope = ikarus_property_scope_to_object_scope(&property_scope); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); + auto property_object_scope = ikarus_property_scope_to_object_scope(property_scope); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); - REQUIRE(ikarus_object_scope_get_type(&blueprint_object_scope) == IkarusObjectScopeType_Blueprint); - REQUIRE(ikarus_object_scope_get_type(&property_object_scope) == IkarusObjectScopeType_Property); - REQUIRE(ikarus_object_scope_get_type(&entity_object_scope) == IkarusObjectScopeType_Entity); + REQUIRE(ikarus_object_scope_get_type(blueprint_object_scope) == IkarusObjectScopeType_Blueprint); + REQUIRE(ikarus_object_scope_get_type(property_object_scope) == IkarusObjectScopeType_Property); + REQUIRE(ikarus_object_scope_get_type(entity_object_scope) == IkarusObjectScopeType_Entity); } TEST_CASE("object_scope_visiting", "[object_scope]") { auto blueprint = IkarusBlueprint{}; auto blueprint_scope = ikarus_blueprint_scope_create(); - auto property_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto property_scope = ikarus_property_scope_create_blueprint(blueprint); auto entity_scope = ikarus_entity_scope_create(); - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); - auto property_object_scope = ikarus_property_scope_to_object_scope(&property_scope); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); + auto property_object_scope = ikarus_property_scope_to_object_scope(property_scope); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); auto scopes = { std::make_pair(blueprint_object_scope, 1), @@ -197,10 +201,10 @@ TEST_CASE("object_scope_visiting", "[object_scope]") { int test = 0; ikarus_object_scope_visit( - &scope, - [](IkarusBlueprintScope const * _, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusPropertyScope const * _, void * data) { *reinterpret_cast(data) = 2; }, - [](IkarusEntityScope const * _, void * data) { *reinterpret_cast(data) = 3; }, + scope, + [](IkarusBlueprintScope, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusPropertyScope, void * data) { *reinterpret_cast(data) = 2; }, + [](IkarusEntityScope, void * data) { *reinterpret_cast(data) = 3; }, &test ); diff --git a/src/types/object_type.cpp b/src/types/object_type.cpp deleted file mode 100644 index 7f5e377..0000000 --- a/src/types/object_type.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include - -#include - -IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type) { - return static_cast(type); -} - -IkarusObjectTypes ikarus_object_type_to_bitset(IkarusObjectType type) { - if (type == 0) { - return static_cast(0); - } - - return static_cast(1 << type); -} - -TEST_CASE("folder_to_object_type_conversion", "[object_type]") { - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_None) == IkarusObjectType_None); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_BlueprintFolder) == IkarusObjectType_BlueprintFolder); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_PropertyFolder) == IkarusObjectType_PropertyFolder); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_EntityFolder) == IkarusObjectType_EntityFolder); -} - -TEST_CASE("object_type_to_bitset_conversion", "[object_type]") { - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_None) == IkarusObjectTypes_None); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Blueprint) == IkarusObjectTypes_Blueprint); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Property) == IkarusObjectTypes_Property); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Entity) == IkarusObjectTypes_Entity); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_BlueprintFolder) == IkarusObjectTypes_BlueprintFolder); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_PropertyFolder) == IkarusObjectTypes_PropertyFolder); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_EntityFolder) == IkarusObjectTypes_EntityFolder); -} diff --git a/src/types/value.cpp b/src/values/value.cpp similarity index 96% rename from src/types/value.cpp rename to src/values/value.cpp index 251db2c..56fa9c5 100644 --- a/src/types/value.cpp +++ b/src/values/value.cpp @@ -1,10 +1,16 @@ -#include "ikarus/types/value.h" +#include "ikarus/values/value.h" #include +#include +#include #include +#include #include +#include +#include + /// \brief Creates an indeterminate entity value of a given type. /// \param type The type of the value. /// \return The entity value. @@ -39,7 +45,7 @@ IkarusEntityValue ikarus_value_create_number(long double value) { IkarusEntityValue ikarus_value_create_text(char const * value) { if (value == nullptr) { return value_create_invalid(IkarusPropertyType_Text); - }; + } return IkarusEntityValue{ ._type = IkarusPropertyType_Text, @@ -326,9 +332,9 @@ TEST_CASE("visit_value", "[value]") { ikarus_value_visit( &value, - [](IkarusToggleValue const * _, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusNumberValue const * _, void * data) { *reinterpret_cast(data) = 2; }, - [](IkarusTextValue const * _, void * data) { *reinterpret_cast(data) = 3; }, + [](IkarusToggleValue const *, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusNumberValue const *, void * data) { *reinterpret_cast(data) = 2; }, + [](IkarusTextValue const *, void * data) { *reinterpret_cast(data) = 3; }, &test ); From 521c61c8fb460d12e456a120fb259763581903cf Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 1 Nov 2023 18:03:03 +0100 Subject: [PATCH 006/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index afe7b16..416e326 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit afe7b165002ccf86a37da5b6b157ce4ff9db0401 +Subproject commit 416e326c60f3763a43dec8c66e58616ab50d1a2a From eb1f414fc4e36f6241817d33bf69346d61d27433 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 6 Nov 2023 13:14:39 +0100 Subject: [PATCH 007/166] finalise interface & documentation Signed-off-by: Folling --- .clang-tidy | 26 +++ CMakeLists.txt | 49 ++-- LICENSE.md | 22 +- docs/DoxyFile | 7 +- docs/enum_format_fix.js | 11 - docs/format_fix.js | 68 ++++++ docs/header.html | 3 +- include/ikarus/folders/blueprint_folder.h | 143 +++++++++++- include/ikarus/folders/blueprint_tree_item.h | 31 +++ include/ikarus/folders/entity_folder.h | 143 +++++++++++- include/ikarus/folders/entity_tree_item.h | 31 +++ include/ikarus/folders/folder.h | 62 +---- include/ikarus/folders/folder_type.h | 10 +- include/ikarus/folders/property_folder.h | 156 ++++++++++++- include/ikarus/folders/property_tree_item.h | 31 +++ include/ikarus/global.h | 16 ++ include/ikarus/id.h | 66 ------ include/ikarus/objects/blueprint.h | 201 ++++++++++++++++- include/ikarus/objects/entity.h | 226 ++++++++++++++++++- include/ikarus/objects/object.h | 109 ++------- include/ikarus/objects/object_type.h | 38 ++-- include/ikarus/objects/property.h | 199 ++++++++++++++-- include/ikarus/objects/property_source.h | 47 ++++ include/ikarus/objects/property_type.h | 24 +- include/ikarus/objects/property_type_info.h | 79 +++++++ include/ikarus/project/project.h | 179 +++++++++++++++ include/ikarus/scopes/blueprint_scope.h | 16 +- include/ikarus/scopes/entity_scope.h | 19 +- include/ikarus/scopes/object_scope.h | 66 ++---- include/ikarus/scopes/property_scope.h | 57 ++--- include/ikarus/stdtypes.h | 1 + include/ikarus/values/value.h | 182 ++++++--------- src/CMakeLists.txt | 1 + src/folders/blueprint_folder.hpp | 8 + src/folders/entity_folder.hpp | 8 + src/folders/folder.hpp | 13 ++ src/folders/property_folder.hpp | 8 + src/id.cpp | 26 +-- src/id.hpp | 40 ++++ src/objects/blueprint.hpp | 8 + src/objects/entity.hpp | 8 + src/objects/object.cpp | 10 +- src/objects/object.hpp | 15 ++ src/objects/object_type.cpp | 16 -- src/objects/property.hpp | 8 + src/projects/project.hpp | 10 + src/scopes/blueprint_scope.cpp | 12 + src/scopes/blueprint_scope.hpp | 5 + src/scopes/entity_scope.cpp | 12 + src/scopes/entity_scope.hpp | 5 + src/scopes/object_scope.cpp | 205 ++++------------- src/scopes/object_scope.hpp | 11 + src/scopes/property_scope.cpp | 35 +++ src/scopes/property_scope.hpp | 12 + src/values/value.cpp | 58 ++--- vendor/sqlitecpp | 2 +- 56 files changed, 2074 insertions(+), 780 deletions(-) create mode 100644 .clang-tidy delete mode 100644 docs/enum_format_fix.js create mode 100644 docs/format_fix.js create mode 100644 include/ikarus/folders/blueprint_tree_item.h create mode 100644 include/ikarus/folders/entity_tree_item.h create mode 100644 include/ikarus/folders/property_tree_item.h create mode 100644 include/ikarus/global.h delete mode 100644 include/ikarus/id.h create mode 100644 include/ikarus/objects/property_source.h create mode 100644 include/ikarus/objects/property_type_info.h create mode 100644 include/ikarus/project/project.h create mode 100644 src/folders/blueprint_folder.hpp create mode 100644 src/folders/entity_folder.hpp create mode 100644 src/folders/folder.hpp create mode 100644 src/folders/property_folder.hpp create mode 100644 src/id.hpp create mode 100644 src/objects/blueprint.hpp create mode 100644 src/objects/entity.hpp create mode 100644 src/objects/object.hpp delete mode 100644 src/objects/object_type.cpp create mode 100644 src/objects/property.hpp create mode 100644 src/projects/project.hpp create mode 100644 src/scopes/blueprint_scope.cpp create mode 100644 src/scopes/blueprint_scope.hpp create mode 100644 src/scopes/entity_scope.cpp create mode 100644 src/scopes/entity_scope.hpp create mode 100644 src/scopes/object_scope.hpp create mode 100644 src/scopes/property_scope.cpp create mode 100644 src/scopes/property_scope.hpp diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..fa1f43f --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,26 @@ +Checks: >- + -*, + bugprone-*, + cppcoreguidelines-*, -cppcoreguidelines-owning-memory, + clang-analyzer-*, + google-*, -google-readability-todo, + modernize-*, -modernize-use-trailing-return-type, + performance-*, -performance-enum-size, + portability-*, + readability-*, -readability-redundant-access-specifiers +CheckOptions: + readability-identifier-length.IgnoredParameterNames: '^(db|rc|id)$' + readability-identifier-length.IgnoredLoopCounterNames: '^[ij]$' + readability-identifier-length.IgnoredVariableNames: '^(db|rc|id)$' + cppcoreguidelines-avoid-do-while.IgnoreMacros: Yes +HeaderFileExtensions: + - h + - hpp + - tpp + - ipp +ImplementationFileExtensions: + - c + - cpp +FormatStyle: file +InheritParentConfig: false +WarningsAsErrors: '*' diff --git a/CMakeLists.txt b/CMakeLists.txt index 5edd8b6..1bbb826 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,9 +3,10 @@ project(ikarus) option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF) option(LIBIKARUS_ENABLE_LINTS "Enable linting" OFF) +option(LIBIKARUS_BUILD_DOCS "Build documentation" OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) @@ -29,11 +30,6 @@ target_include_directories( ${CMAKE_CURRENT_LIST_DIR}/src ) -target_link_libraries( - libikarus PRIVATE - Catch2::Catch2WithMain -) - target_link_libraries( libikarus PRIVATE cppbase @@ -45,35 +41,44 @@ if (LIBIKARUS_ENABLE_LINTS) find_program(CLANG_TIDY_PATH NAMES clang-tidy REQUIRED) set_property(TARGET libikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) - string( - CONCAT CLANG_TIDY_OPTIONS - "-checks=-*," - "bugprone-*," - "concurrency-*," - "cppcoreguidelines-*,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-type-union-access," - "misc-*," - "modernize-*,-modernize-use-trailing-return-type," - "performance-*," - "portability-*," - "readability-*,-readability-identifier-length,-readability-magic-numbers,-readability-function-cognitive-complexity" - ) - set_property( TARGET libikarus - PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY_PATH};${CLANG_TIDY_OPTIONS} + PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY_PATH}; ) endif () if (LIBIKARUS_ENABLE_TESTS) add_executable(libikarus_tests ${SOURCE_FILES}) - target_link_libraries(libikarus_tests PRIVATE Catch2::Catch2WithMain) + target_link_libraries(libikarus_tests PRIVATE sqlitecpp Catch2::Catch2WithMain) target_include_directories( - libikarus_tests PUBLIC + libikarus_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include + ${CMAKE_CURRENT_LIST_DIR}/src ) include(CTest) include(vendor/catch2/extras/Catch.cmake) catch_discover_tests(libikarus_tests) +endif () + +if (LIBIKARUS_BUILD_DOCS) + find_program(DOXYGEN_PATH NAMES doxygen REQUIRED) + add_custom_target( + libikarus_docs + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/docs + COMMAND ${DOXYGEN_PATH} DoxyFile + COMMENT "Generating documentation with Doxygen" + VERBATIM + ) + + add_dependencies( + libikarus + libikarus_docs + ) + + add_dependencies( + libikarus_tests + libikarus_docs + ) endif () \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 598a8d4..5f2c6f6 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,10 +1,20 @@ -Copyright 2019-2023 Folling (mail@folling.io) +Copyright 2019-2023 Folling (folling@ikarus.world) -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following + disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/docs/DoxyFile b/docs/DoxyFile index 61f3460..1c5a659 100644 --- a/docs/DoxyFile +++ b/docs/DoxyFile @@ -6,11 +6,14 @@ FULL_SIDEBAR = NO GENERATE_LATEX = NO GENERATE_TREEVIEW = YES HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5 -HTML_EXTRA_FILES = ../vendor/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js ../vendor/doxygen-awesome-css/doxygen-awesome-fragment-copy-button.js ./enum_format_fix.js +HTML_EXTRA_FILES = ../vendor/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js ../vendor/doxygen-awesome-css/doxygen-awesome-fragment-copy-button.js ./format_fix.js HTML_EXTRA_STYLESHEET = ../vendor/doxygen-awesome-css/doxygen-awesome.css HTML_HEADER = header.html INPUT = .. OUTPUT_DIRECTORY = generated PROJECT_BRIEF = A C-API implementation for Ikarus, a tool for worldbuilding. PROJECT_NAME = LIBIKARUS -RECURSIVE = YES \ No newline at end of file +RECURSIVE = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBER_DOCS = NO +EXTRACT_PRIVATE = NO diff --git a/docs/enum_format_fix.js b/docs/enum_format_fix.js deleted file mode 100644 index 2507ab8..0000000 --- a/docs/enum_format_fix.js +++ /dev/null @@ -1,11 +0,0 @@ -// maximum efficiency -function enumFormatFix() { - Array.from(document.getElementsByClassName("memItemRight")).forEach((elem) => { - elem.innerHTML = elem.innerHTML.replaceAll("
", ""); - elem.innerHTML = elem.innerHTML.replaceAll(" ", ""); - elem.innerHTML = elem.innerHTML.replaceAll("{", "{
    "); - elem.innerHTML = elem.innerHTML.replaceAll("\n,", ","); - elem.innerHTML = elem.innerHTML.replaceAll(",", ",
    "); - elem.innerHTML = elem.innerHTML.replaceAll("}", "
}"); - }); -} \ No newline at end of file diff --git a/docs/format_fix.js b/docs/format_fix.js new file mode 100644 index 0000000..d7db696 --- /dev/null +++ b/docs/format_fix.js @@ -0,0 +1,68 @@ +// maximum efficiency +function enumFormatFix() { + Array.from(document.getElementsByClassName("memItemRight")).forEach((elem) => { + if (elem.innerHTML.includes("{")) { + let str = elem.innerHTML; + + str = str.replaceAll("
", ""); + str = str.replaceAll(" ", ""); + str = str.replaceAll("{", "{
    "); + str = str.replaceAll("\n,", ","); + str = str.replaceAll(",", ",
    "); + str = str.replaceAll("}", "
}"); + + elem.innerHTML = str + } + }); +} + +function paramFormatFix() { + Array.from(document.getElementsByClassName("memItemRight")).forEach((elem) => { + if (elem.innerHTML.includes("(")) { + let str = elem.innerHTML; + + let res = ""; + let bracket_level = 0; + let hit_comma = false; + + for (let c of str) { + let new_hit_comma = false; + + if (c === '(') { + if (bracket_level === 0) { + res += "(
    "; + } else { + res += '('; + } + + bracket_level++; + } else if (c === ')') { + if (bracket_level === 1) { + res += "
)"; + } else { + res += ')'; + } + + bracket_level--; + } else if (c === ',') { + if (bracket_level === 1) { + res += ",
    "; + } else { + res += ','; + } + + new_hit_comma = true; + } else if (c === ' ' && hit_comma && bracket_level === 1) { + new_hit_comma = false; + // skip this space + } else { + res += c; + } + + hit_comma = new_hit_comma; + } + + elem.innerHTML = res + } + }); +} \ No newline at end of file diff --git a/docs/header.html b/docs/header.html index dfce9a1..fbe464c 100644 --- a/docs/header.html +++ b/docs/header.html @@ -29,10 +29,11 @@ - + $treeview diff --git a/include/ikarus/folders/blueprint_folder.h b/include/ikarus/folders/blueprint_folder.h index 8ab973e..430c147 100644 --- a/include/ikarus/folders/blueprint_folder.h +++ b/include/ikarus/folders/blueprint_folder.h @@ -1,21 +1,150 @@ #pragma once /// \file blueprint_folder.h -/// \author Folling +/// \author Folling -#include #include -/// \addtogroup folder Folders +/// \addtogroup blueprints Blueprints /// @{ IKARUS_BEGIN_HEADER /// \brief A blueprint folder, storing blueprints and other blueprint folders. -struct IkarusBlueprintFolder { - /// \private \brief The id of the blueprint folder. - IkarusId id; -}; +struct IkarusBlueprintFolder; + +/// \brief Creates a blueprint folder. +/// \param project The project the blueprint folder is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the blueprint folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created blueprint folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusBlueprintFolder * ikarus_blueprint_folder_create( + struct IkarusProject * project, IkarusBlueprintFolder * parent, size_t position, char const * name +); + +/// \brief Copies a blueprint folder. +/// \details Creates a copy of the blueprint folder without its children. +/// \param blueprint_folder The blueprint folder to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the blueprint folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created blueprint folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusBlueprintFolder * ikarus_blueprint_folder_copy( + IkarusBlueprintFolder * blueprint_folder, IkarusBlueprintFolder * parent, size_t position, char const * name +); + +/// \brief Deletes a blueprint folder and all its children +/// \param blueprint_folder The blueprint folder to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param keep_children If true, the children of the blueprint folder will be moved to the parent folder. +IKA_API void ikarus_blueprint_folder_delete(IkarusBlueprintFolder * blueprint_folder, bool keep_children); + +/// \brief Gets the project of a blueprint folder. +/// \param blueprint_folder The blueprint folder to get the project of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The project of the blueprint folder or null if an error occurs. +IKA_API struct IkarusProject * ikarus_blueprint_folder_get_project(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Gets the parent folder of a blueprint folder. +/// \param blueprint_folder The blueprint folder to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the blueprint folder or null if an error occurs. +IKA_API struct IkarusBlueprintFolder * ikarus_blueprint_folder_get_parent(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Gets the position of a blueprint folder within its parent folder. +/// \param blueprint_folder The blueprint folder to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the blueprint folder or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_folder_get_position(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Gets the name of a blueprint folder. +/// \param blueprint_folder The blueprint folder to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the blueprint folder or null if an error occurs. +/// \remark The returned pointer is valid until the blueprint folder is freed but may be invalidated by other operations. +IKA_API char const * ikarus_blueprint_folder_get_name(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Gets the number of children of a blueprint folder. +/// \param blueprint_folder The blueprint folder to get the number of children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of children or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_folder_get_child_count(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Gets the children of a blueprint folder. +/// \param blueprint_folder The blueprint folder to get the children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param children_out The buffer to write the children to. +/// \pre \li Must not be null. +/// \param children_out_size The size of the buffer. +IKA_API void ikarus_blueprint_folder_get_children( + IkarusBlueprintFolder const * blueprint_folder, struct IkarusBlueprintTreeItem ** children_out, size_t children_out_size +); + +/// \brief Sets the parent folder of an blueprint folder. +/// \param blueprint_folder The blueprint folder to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the blueprint folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_blueprint_folder_set_parent( + IkarusBlueprintFolder * blueprint_folder, struct IkarusEntityFolder * new_parent, size_t new_position +); + +/// \brief Sets the position of an blueprint folder within its parent folder. +/// \param blueprint_folder The blueprint folder to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the blueprint folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_blueprint_folder_set_position(IkarusBlueprintFolder * blueprint_folder, size_t new_position); + +/// \brief Sets the name of an blueprint folder. +/// \param blueprint_folder The blueprint folder to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_blueprint_folder_set_name(IkarusBlueprintFolder * blueprint_folder, char const * new_name); + +/// \brief Converts a blueprint folder to a generic folder. +/// \param blueprint_folder The blueprint folder to convert. +/// \return The constructed folder, representing the blueprint folder. +IKA_API struct IkarusFolder * ikarus_blueprint_folder_to_folder(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Converts a blueprint folder to an object. +/// \param blueprint_folder The blueprint folder to convert. +/// \return The constructed object, representing the blueprint folder. +IKA_API struct IkarusObject * ikarus_blueprint_folder_to_object(IkarusBlueprintFolder const * blueprint_folder); IKARUS_END_HEADER diff --git a/include/ikarus/folders/blueprint_tree_item.h b/include/ikarus/folders/blueprint_tree_item.h new file mode 100644 index 0000000..5a09dcc --- /dev/null +++ b/include/ikarus/folders/blueprint_tree_item.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file blueprint_tree_item.h +/// \author Folling + +#include + +/// \addtogroup blueprints Blueprints +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusBlueprintTreeItem; + +/// \brief Visits a blueprint tree item, calling the appropriate visitor function. +/// \param item The item to visit. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprint_visitor The visitor function called if the item is a blueprint. Skipped if null. +/// \param blueprint_folder_visitor The visitor function called if the item is a blueprint folder. Skipped if null. +/// \param data The data passed to the visitor functions. +IKA_API void ikarus_blueprint_tree_item_visit( + struct IkarusBlueprintTreeItem * item, + void (*blueprint_visitor)(struct IkarusBlueprint * blueprint, void * data), + void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder * folder, void * data), + void * data +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/folders/entity_folder.h b/include/ikarus/folders/entity_folder.h index 2bffa3f..8c516cb 100644 --- a/include/ikarus/folders/entity_folder.h +++ b/include/ikarus/folders/entity_folder.h @@ -1,21 +1,150 @@ #pragma once /// \file entity_folder.h -/// \author Folling +/// \author Folling -#include #include -/// \addtogroup folder Folders +/// \addtogroup entities Entities /// @{ IKARUS_BEGIN_HEADER /// \brief A entity folder, storing entities and other entity folders. -struct IkarusEntityFolder { - /// \private \brief The id of the entity folder. - IkarusId id; -}; +struct IkarusEntityFolder; + +/// \brief Creates a entity folder. +/// \param project The project the entity folder is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the entity folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created entity folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusEntityFolder * ikarus_entity_folder_create( + struct IkarusProject * project, IkarusEntityFolder * parent, size_t position, char const * name +); + +/// \brief Copies a entity folder. +/// \details Creates a copy of the entity folder without its children. +/// \param entity_folder The entity folder to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the entity folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created entity folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusEntityFolder * ikarus_entity_folder_copy( + IkarusEntityFolder * entity_folder, IkarusEntityFolder * parent, size_t position, char const * name +); + +/// \brief Deletes a entity folder and all its children +/// \param entity_folder The entity folder to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param keep_children If true, the children of the entity folder will be moved to the parent folder. +IKA_API void ikarus_entity_folder_delete(IkarusEntityFolder * entity_folder, bool keep_children); + +/// \brief Gets the project of a entity folder. +/// \param entity_folder The entity folder to get the project of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The project of the entity folder or null if an error occurs. +IKA_API struct IkarusProject * ikarus_entity_folder_get_project(IkarusEntityFolder const * entity_folder); + +/// \brief Gets the parent folder of a entity folder. +/// \param entity_folder The entity folder to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the entity folder or null if an error occurs. +IKA_API struct IkarusEntityFolder * ikarus_entity_folder_get_parent(IkarusEntityFolder const * entity_folder); + +/// \brief Gets the position of a entity folder within its parent folder. +/// \param entity_folder The entity folder to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the entity folder or undefined if an error occurs. +IKA_API size_t ikarus_entity_folder_get_position(IkarusEntityFolder const * entity_folder); + +/// \brief Gets the name of a entity folder. +/// \param entity_folder The entity folder to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the entity folder or null if an error occurs. +/// \remark The returned pointer is valid until the entity folder is freed but may be invalidated by other operations. +IKA_API char const * ikarus_entity_folder_get_name(IkarusEntityFolder const * entity_folder); + +/// \brief Gets the number of children of a entity folder. +/// \param entity_folder The entity folder to get the number of children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of children or undefined if an error occurs. +IKA_API size_t ikarus_entity_folder_get_child_count(IkarusEntityFolder const * entity_folder); + +/// \brief Gets the children of a entity folder. +/// \param entity_folder The entity folder to get the children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param children_out The buffer to write the children to. +/// \pre \li Must not be null. +/// \param children_out_size The size of the buffer. +IKA_API void ikarus_entity_folder_get_children( + IkarusEntityFolder const * entity_folder, struct IkarusEntityTreeItem ** children_out, size_t children_out_size +); + +/// \brief Sets the parent folder of an entity folder. +/// \param entity_folder The entity folder to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the entity folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_entity_folder_set_parent( + IkarusEntityFolder * entity_folder, struct IkarusEntityFolder * new_parent, size_t new_position +); + +/// \brief Sets the position of an entity folder within its parent folder. +/// \param entity_folder The entity folder to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the entity folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_entity_folder_set_position(IkarusEntityFolder * entity_folder, size_t new_position); + +/// \brief Sets the name of an entity folder. +/// \param entity_folder The entity folder to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_entity_folder_set_name(IkarusEntityFolder * entity_folder, char const * new_name); + +/// \brief Converts a entity folder to a generic folder. +/// \param entity_folder The entity folder to convert. +/// \return The constructed folder, representing the entity folder. +IKA_API struct IkarusFolder * ikarus_entity_folder_to_folder(IkarusEntityFolder const * entity_folder); + +/// \brief Converts a entity folder to an object. +/// \param entity_folder The entity folder to convert. +/// \return The constructed object, representing the entity folder. +IKA_API struct IkarusObject * ikarus_entity_folder_to_object(IkarusEntityFolder const * entity_folder); IKARUS_END_HEADER diff --git a/include/ikarus/folders/entity_tree_item.h b/include/ikarus/folders/entity_tree_item.h new file mode 100644 index 0000000..516455f --- /dev/null +++ b/include/ikarus/folders/entity_tree_item.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file entity_tree_item.h +/// \author Folling + +#include + +/// \addtogroup entities Entities +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusEntityTreeItem; + +/// \brief Visits a entity tree item, calling the appropriate visitor function. +/// \param item The item to visit. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param entity_visitor The visitor function called if the item is a entity. Skipped if null. +/// \param entity_folder_visitor The visitor function called if the item is a entity folder. Skipped if null. +/// \param data The data passed to the visitor functions. +IKA_API void ikarus_entity_tree_item_visit( + struct IkarusEntityTreeItem * item, + void (*entity_visitor)(struct IkarusEntity * entity, void * data), + void (*entity_folder_visitor)(struct IkarusEntityFolder * folder, void * data), + void * data +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/folders/folder.h b/include/ikarus/folders/folder.h index 0cdf946..d0d60e2 100644 --- a/include/ikarus/folders/folder.h +++ b/include/ikarus/folders/folder.h @@ -1,7 +1,7 @@ #pragma once /// \file folder.h -/// \author Folling +/// \author Folling #include #include @@ -15,61 +15,23 @@ IKARUS_BEGIN_HEADER /// \brief Folders are used to group objects together. /// @{ -/// \private \brief The data of a folder. -union IkarusFolderData { - /// \private \brief The blueprint folder data of the folder. - IkarusBlueprintFolder blueprint_folder; - /// \private \brief The property folder data of the folder. - IkarusPropertyFolder property_folder; - /// \private \brief The entity folder data of the folder. - IkarusEntityFolder entity_folder; -}; - /// \brief A generic folder. Similar to how Objects wrap all types of objects, Folders wrap all types of folders. -struct IkarusFolder { - /// \private \brief The data of the folder. - IkarusFolderData data; +struct IkarusFolder; - /// \private \brief The type of the folder. - IkarusFolderType type; -}; - -/// \brief Constructs a folder from a blueprint folder. -/// \param blueprint_folder The blueprint folder to construct the folder from. -/// \return The constructed folder. -IKA_API IkarusFolder ikarus_folder_from_blueprint_folder(IkarusBlueprintFolder blueprint_folder); -/// \brief Constructs a folder from a property folder. -/// \param property_folder The property folder to construct the folder from. -/// \return The constructed folder. -IKA_API IkarusFolder ikarus_folder_from_property_folder(IkarusPropertyFolder property_folder); -/// \brief Constructs a folder from an entity folder. -/// \param entity_folder The entity folder to construct the folder from. -/// \return The constructed folder. -IKA_API IkarusFolder ikarus_folder_from_entity_folder(IkarusEntityFolder entity_folder); - -/// \brief Fetches the folder type of a folder. -/// \param folder The folder to fetch the type of. -/// \return The type of the folder. -IKA_API IkarusFolderType ikarus_folder_get_type(IkarusFolder folder); - -/// \brief Checks if two folders are equal. -/// \details Since ids store the type of the object, this boils down to a simple comparison of the ids. -/// \param left The left side of the comparison. -/// \param right The right side of the comparison. -/// \return True if the folders are equal, false otherwise -IKA_API bool ikarus_folder_is_equal(IkarusFolder left, IkarusFolder right); +/// \brief Special value for inserting objects at the end of a folder. +enum FolderPosition { FolderPosition_EndOfFolder = -1 }; /// \brief Visits a folder. Calling the appropriate function for the folder's type. /// \param folder The folder to visit. -/// \param blueprint The function to call if the folder is a blueprint folder. -/// \param property The function to call if the folder is a property folder. -/// \param entity The function to call if the folder is an entity folder. -/// \param data The data to pass to the functions. +/// \param blueprint_visitor The function to call if the folder is a blueprint folder. +/// \param property_visitor The function to call if the folder is a property folder. +/// \param entity_visitor The function to call if the folder is an entity folder. +/// \param data The data passed to the visitor functions. IKA_API void ikarus_folder_visit( - IkarusFolder folder, - void (*blueprint)(IkarusBlueprintFolder, void *), - void (*property)(IkarusPropertyFolder, void *), - void (*entity)(IkarusEntityFolder, void *), + IkarusFolder * folder, + void (*blueprint_visitor)(IkarusBlueprintFolder *, void *), + void (*property_visitor)(IkarusPropertyFolder *, void *), + void (*entity_visitor)(IkarusEntityFolder *, void *), void * data ); diff --git a/include/ikarus/folders/folder_type.h b/include/ikarus/folders/folder_type.h index 2a63e46..d3ac05e 100644 --- a/include/ikarus/folders/folder_type.h +++ b/include/ikarus/folders/folder_type.h @@ -3,7 +3,7 @@ // IMPLEMENTATION_DETAIL_FOLDER_TYPES /// \file folder_type.h -/// \author Folling +/// \author Folling #include @@ -11,16 +11,16 @@ /// @{ /// \brief The type of an folder. -/// \remark Folders have the 8th bit set. +/// \remark The values are identical to their counterparts in #IkarusObjectType. enum IkarusFolderType { /// \brief Not a folder or no folder. IkarusFolderType_None = 0, /// \brief An IkarusBlueprintFolder - IkarusFolderType_BlueprintFolder = 0b1000'0001, + IkarusFolderType_BlueprintFolder = 0b0100'0001, /// \brief An IkarusPropertyFolder - IkarusFolderType_PropertyFolder = 0b1000'0010, + IkarusFolderType_PropertyFolder = 0b0100'0010, /// \brief An IkarusEntityFolder - IkarusFolderType_EntityFolder = 0b1000'0011, + IkarusFolderType_EntityFolder = 0b0100'0011, }; /// @} diff --git a/include/ikarus/folders/property_folder.h b/include/ikarus/folders/property_folder.h index 8e6709f..d55d6cf 100644 --- a/include/ikarus/folders/property_folder.h +++ b/include/ikarus/folders/property_folder.h @@ -1,22 +1,158 @@ #pragma once -#include +/// \file property_folder.h +/// \author Folling + #include -/// \file property_folder.h -/// \author Folling - -/// \addtogroup folder Folders +/// \addtogroup properties Properties /// @{ IKARUS_BEGIN_HEADER /// \brief A property folder, storing properties and other property folders. -/// \remark Property folders are scoped to the blueprint or entity they are associated with. -struct IkarusPropertyFolder { - /// \private \brief The id of the property folder. - IkarusId id; -}; +struct IkarusPropertyFolder; + +/// \brief Creates a property folder. +/// \param project The project the property folder is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the property folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created property folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusPropertyFolder * ikarus_property_folder_create( + struct IkarusProject * project, IkarusPropertyFolder * parent, size_t position, char const * name +); + +/// \brief Copies a property folder. +/// \details Creates a copy of the property folder without its children. +/// \param property_folder The property folder to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the property folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created property folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusPropertyFolder * ikarus_property_folder_copy( + IkarusPropertyFolder * property_folder, IkarusPropertyFolder * parent, size_t position, char const * name +); + +/// \brief Deletes a property folder and all its children +/// \param property_folder The property folder to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param keep_children If true, the children of the property folder will be moved to the parent folder. +IKA_API void ikarus_property_folder_delete(IkarusPropertyFolder * property_folder, bool keep_children); + +/// \brief Gets the project of a property folder. +/// \param property_folder The property folder to get the project of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The project of the property folder or null if an error occurs. +IKA_API struct IkarusProject * ikarus_property_folder_get_project(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the property source of a property folder. +/// \param property_folder The property folder to get the property source of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The property source of the property folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertySource * ikarus_property_folder_get_source(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the parent folder of a property folder. +/// \param property_folder The property folder to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the property folder or null if an error occurs. +IKA_API struct IkarusPropertyFolder * ikarus_property_folder_get_parent(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the position of a property folder within its parent folder. +/// \param property_folder The property folder to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the property folder or undefined if an error occurs. +IKA_API size_t ikarus_property_folder_get_position(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the name of a property folder. +/// \param property_folder The property folder to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the property folder or null if an error occurs. +/// \remark The returned pointer is valid until the property folder is freed but may be invalidated by other operations. +IKA_API char const * ikarus_property_folder_get_name(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the number of children of a property folder. +/// \param property_folder The property folder to get the number of children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of children or undefined if an error occurs. +IKA_API size_t ikarus_property_folder_get_child_count(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the children of a property folder. +/// \param property_folder The property folder to get the children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param children_out The buffer to write the children to. +/// \pre \li Must not be null. +/// \param children_out_size The size of the buffer. +IKA_API void ikarus_property_folder_get_children( + IkarusPropertyFolder const * property_folder, struct IkarusPropertyTreeItem ** children_out, size_t children_out_size +); + +/// \brief Sets the parent folder of an property folder. +/// \param property_folder The property folder to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the property folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_property_folder_set_parent( + IkarusPropertyFolder * property_folder, struct IkarusPropertyFolder * new_parent, size_t new_position +); + +/// \brief Sets the position of an property folder within its parent folder. +/// \param property_folder The property folder to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the property folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_property_folder_set_position(IkarusPropertyFolder * property_folder, size_t new_position); + +/// \brief Sets the name of an property folder. +/// \param property_folder The property folder to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_property_folder_set_name(IkarusPropertyFolder * property_folder, char const * new_name); + +/// \brief Converts a property folder to a generic folder. +/// \param property_folder The property folder to convert. +/// \return The constructed folder, representing the property folder. +IKA_API struct IkarusFolder * ikarus_property_folder_to_folder(IkarusPropertyFolder const * property_folder); + +/// \brief Converts a property folder to an object. +/// \param property_folder The property folder to convert. +/// \return The constructed object, representing the property folder. +IKA_API struct IkarusObject * ikarus_property_folder_to_object(IkarusPropertyFolder const * property_folder); IKARUS_END_HEADER diff --git a/include/ikarus/folders/property_tree_item.h b/include/ikarus/folders/property_tree_item.h new file mode 100644 index 0000000..0b37c2c --- /dev/null +++ b/include/ikarus/folders/property_tree_item.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file property_tree_item.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusPropertyTreeItem; + +/// \brief Visits a property tree item, calling the appropriate visitor function. +/// \param item The item to visit. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property_visitor The visitor function called if the item is a property. Skipped if null. +/// \param property_folder_visitor The visitor function called if the item is a property folder. Skipped if null. +/// \param data The data passed to the visitor functions. +IKA_API void ikarus_property_tree_item_visit( + struct IkarusPropertyTreeItem * item, + void (*property_visitor)(struct IkarusProperty * property, void * data), + void (*property_folder_visitor)(struct IkarusPropertyFolder * folder, void * data), + void * data +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/global.h b/include/ikarus/global.h new file mode 100644 index 0000000..6d91227 --- /dev/null +++ b/include/ikarus/global.h @@ -0,0 +1,16 @@ +#pragma once + +/// \file memory.h +/// \author Folling + +#include + +/// \addtogroup global Global +/// \brief Information relevant to the entire library. +/// @{ + +/// \brief Frees a pointer allocated by ikarus. Every pointer returned by a function must be freed using this function unless +/// explicitly stated otherwise. +IKA_API void ikarus_free(void * ptr); + +/// @} diff --git a/include/ikarus/id.h b/include/ikarus/id.h deleted file mode 100644 index d184e63..0000000 --- a/include/ikarus/id.h +++ /dev/null @@ -1,66 +0,0 @@ -// IMPLEMENTATION_DETAIL_DATABASE - -/// \file id.h -/// \author Folling - -#pragma once - -#include -#include -#include - -IKARUS_BEGIN_HEADER - -/// \defgroup id Ids -/// \brief Ids are used to identify objects in the database. -/// \details They are stored as 64 bit integers with the following layout: -/// - first 8 bits: #IkarusObjectType - 255 possible values, 0 for special values -/// - last 56 bits: incremented counter generated by the database -/// @{ - -/// \brief A wrapper around a 64 bit integer that represents the id of an object. -/// \details They are stored as 64 bit integers with the following layout: -/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. -/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. -/// - next 7 bits: #IkarusObjectType - 127 possible values, 0 for special values -/// - last 56 bits: incremented counter generated by the database -struct IkarusId { - /// \private \brief The value of the id. - int64_t data; -}; - -/// \brief A special id returned by failed functions. -IkarusId const IKARUS_ID_NONE{0}; -/// \brief A special id used to indicate an optional id not being specified. -IkarusId const IKARUS_ID_UNSPECIFIED{1}; - -/// \private \brief Generates a new id for the given object type. -/// \param data The data from which the id will be constructed. -/// \return The generated id. -/// \pre data must be valid under the format described in Id. It should also point to an object in the database. -IkarusId ikarus_id_from_data(int64_t data); - -/// \brief Checkes whether two ids are equal -/// \param left the left side of the comparison -/// \param right the right side of the comparison -/// \return True if the bits from the left id are equal to the bits of the right id -bool ikarus_id_is_equal(IkarusId left, IkarusId right); - -/// \brief Fetches the object type of the given id. -/// \param id The id to fetch the object type for. -/// \return The object type of the given id. -IKA_API IkarusObjectType ikarus_id_get_object_type(IkarusId id); - -/// \brief Checks if the given id is IKARUS_ID_NONE. -/// \param id The id to check. -/// \return True if the id is IKARUS_ID_NONE, false otherwise. -IKA_API bool ikarus_id_is_none(IkarusId id); - -/// \brief Checks if the given id is IKARUS_ID_UNSPECIFIED. -/// \param id The id to check. -/// \return True if the id is IKARUS_ID_UNSPECIFIED, false otherwise. -IKA_API bool ikarus_id_is_unspecified(IkarusId id); - -/// @} - -IKARUS_END_HEADER diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index bd0abd3..e47b1df 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -1,23 +1,210 @@ #pragma once /// \file blueprint.h -/// \author Folling +/// \author Folling -#include #include +#include /// \defgroup blueprints Blueprints /// \brief Blueprints are templates for entities. +/// @{ IKARUS_BEGIN_HEADER /// \brief A blueprint object. /// \details A blueprint is a collection of properties which can be linked to entities. -/// Each entity the blueprint is linked to will have values for the blueprints properties. -struct IkarusBlueprint { - /// \private \brief The id of the blueprint. - IkarusId id; -}; +/// Each entity the blueprint is linked to will have values for the blueprints properties.q +struct IkarusBlueprint; + +/// \brief Creates a blueprint. +/// \param project The project the blueprint is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the blueprint in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created blueprint or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusBlueprint * ikarus_blueprint_create( + struct IkarusProject * project, struct IkarusBlueprintFolder * parent, size_t position, char const * name +); + +/// \brief Creates a blueprint from an entity. +/// \details The created blueprint will have the same properties as the entity. +/// \param entity The entity to create the blueprint from. +/// \pre \li Must not be null. +/// \param link_entity If true, the entity will be linked to the blueprint. If not they will remain separate. +/// \param parent The parent folder of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the blueprint in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the blueprint. Must not be empty. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created blueprint or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusBlueprint * ikarus_blueprint_create_from_entity( + struct IkarusEntity * entity, bool link_entity, struct IkarusBlueprintFolder * parent, size_t position, char const * name +); + +/// \brief Copies a blueprint. +/// \details Creates a deep copy of the blueprint including all of its properties. +/// \param blueprint The blueprint to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the blueprint in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created blueprint or null if an error occurs. +/// \remark Linked entities won't be copied. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusBlueprint * ikarus_blueprint_copy( + IkarusBlueprint const * blueprint, struct IkarusBlueprintFolder * parent, size_t position, char const * name +); + +/// \brief Deletes a blueprint. +/// \param blueprint The blueprint to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The blueprint must not be accessed after deletion. +IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); + +/// \brief Gets the project of a blueprint. +/// \param blueprint The blueprint to get the project of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The project of the blueprint or null if an error occurs. +IKA_API struct IkarusProject * ikarus_blueprint_get_project(IkarusBlueprint const * blueprint); + +/// \brief Gets the parent folder of a blueprint. +/// \param blueprint The blueprint to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the blueprint or null if an error occurs. +IKA_API struct IkarusBlueprintFolder * ikarus_blueprint_get_parent(IkarusBlueprint const * blueprint); + +/// \brief Gets the position of a blueprint within its parent folder. +/// \param blueprint The blueprint to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the blueprint or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_get_position(IkarusBlueprint const * blueprint); + +/// \brief Gets the name of a blueprint. +/// \param blueprint The blueprint to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the blueprint or null if an error occurs. +/// \remark The returned pointer is valid until the blueprint is freed but may be invalidated by other operations. +IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint); + +/// \brief Gets the property root folder of a blueprint. +/// \param blueprint The blueprint to get the root folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The root folder of all properties of the blueprint or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertyFolder * ikarus_blueprint_get_property_root_folder(IkarusBlueprint const * blueprint); + +/// \brief Gets the number of properties of a blueprint. +/// \param blueprint The blueprint to get the number of properties of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of properties or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint); + +/// \brief Gets the properties of a blueprint. +/// \param blueprint The blueprint to get the properties of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param properties_out The buffer to write the properties to. +/// \pre \li Must not be null. +/// \param properties_out_size The size of the buffer. +IKA_API void ikarus_blueprint_get_properties( + IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size +); + +/// \brief Gets the number of entities linked to a blueprint. +/// \param blueprint The blueprint to get the number of linked entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of linked entities or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint); + +/// \brief Gets the entities linked to a blueprint. +/// \param blueprint The blueprint to get the linked entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param entities_out The buffer to write the entities to. +/// \pre \li Must not be null. +/// \param entities_out_size The size of the buffer. +IKA_API void ikarus_blueprint_get_linked_entities( + IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size +); + +/// \brief Sets the parent folder of a blueprint. +/// \param blueprint The blueprint to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the blueprint in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_blueprint_set_parent( + IkarusBlueprint * blueprint, struct IkarusBlueprintFolder * new_parent, size_t new_position +); + +/// \brief Sets the position of a blueprint within its parent folder. +/// \param blueprint The blueprint to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the blueprint. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_blueprint_set_position(IkarusBlueprint * blueprint, size_t new_position); + +/// \brief Sets the name of a blueprint. +/// \param blueprint The blueprint to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_blueprint_set_name(IkarusBlueprint * blueprint, char const * new_name); + +/// \brief Converts a blueprint to an object. +/// \param blueprint The blueprint to convert. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The constructed object, representing the blueprint. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint const * blueprint); + +/// \brief Compares two blueprints. +/// \param left The left blueprint to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param right The right blueprint to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return True if the two blueprints are equal, false otherwise. +/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two +/// blueprints reference the same blueprint in the same project. +IKA_API bool ikarus_blueprint_is_equal(IkarusBlueprint const * left, IkarusBlueprint const * right); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 980c03e..a24cf79 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,10 +1,9 @@ #pragma once -#include #include /// \file entity.h -/// \author Folling +/// \author Folling /// \defgroup entities Entities /// \brief Entities are the core building blocks of Ikarus. @@ -17,7 +16,7 @@ IKARUS_BEGIN_HEADER /// /// Properties can be associated with Entities in two ways: /// - Directly: The property is linked to the entity. -/// - Indirectly: The property is linked to a blueprint of the entity. +/// - Indirectly: The property is linked to a blueprint the entity is linked to. /// /// For each property an entity is linked to, it has a value. These values depend on the property's type. /// For more information on the types see the property documentation. @@ -31,10 +30,223 @@ IKARUS_BEGIN_HEADER /// \remark Values are guaranteed to be in valid format for a given type /// but not guaranteed to be valid under the settings of the property. /// This is because changing the settings can invalidate existing values without resetting them. -struct IkarusEntity { - /// \private \brief The ID of the entity. - IkarusId id; -}; +struct IkarusEntity; + +/// \brief Creates an entity. +/// \param project The project the entity is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the entity. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the entity in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the entity. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param blueprints Blueprints to link the entity to (0..n). Null is treated as an empty array. +/// \param blueprints_count The number of blueprints. +/// \return The created entity or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusEntity * ikarus_entity_create( + struct IkarusProject * project, + struct IkarusEntityFolder * parent, + size_t position, + char const * name, + struct IkarusBlueprint ** blueprints, + size_t blueprints_count +); + +/// \brief Copies an entity. +/// \details Creates a deep copy of the entity including all of its properties & associated values. +/// \param entity The entity to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the entity. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the entity in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the entity. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created entity or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusEntity * ikarus_entity_copy( + struct IkarusEntity * entity, struct IkarusEntityFolder * parent, size_t position, char const * name +); + +/// \brief Deletes an entity. +/// \param entity The entity to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The entity must not be accessed after deletion. +IKA_API void ikarus_entity_delete(IkarusEntity * entity); + +IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint); + +/// \brief Checks if an entity has a specific property. +/// \param entity The entity to check. +/// \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 exist. +/// \return True if the entity has the property, false otherwise. +IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property); + +/// \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. +/// \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); + +/// \brief Unlinks an entity from a blueprint. All values of the properties of the blueprint the entity is linked with 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. +/// \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); + +/// \brief Gets the project of an entity. +/// \param entity The entity to get the project of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The project of the entity or null if an error occurs. +IKA_API struct IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity); + +/// \brief Gets the parent folder of an entity. +/// \param entity The entity to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the entity or null if an error occurs. +IKA_API struct IkarusEntityFolder * ikarus_entity_get_parent(IkarusEntity const * entity); + +/// \brief Gets the position of an entity within its parent folder. +/// \param entity The entity to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the entity or undefined if an error occurs. +IKA_API size_t ikarus_entity_get_position(IkarusEntity const * entity); + +/// \brief Gets the name of an entity. +/// \param entity The entity to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the entity or null if an error occurs. +/// \remark The returned pointer is valid until the entity is freed but may be invalidated by other operations. +IKA_API char const * ikarus_entity_get_name(IkarusEntity const * entity); + +/// \brief Gets the property root folder of an entity. +/// \param entity The entity to get the root folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The root folder of all properties of the entity or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertyFolder * ikarus_entity_get_property_root_folder(IkarusEntity const * entity); + +/// \brief Gets the number of properties of an entity. +/// \param entity The entity to get the number of properties of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of properties or undefined if an error occurs. +IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity); + +/// \brief Gets the properties of an entity. +/// \param entity The entity to get the properties of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param properties_out The buffer to write the properties to. +/// \pre \li Must not be null. +/// \param properties_out_size The size of the buffer. +IKA_API void ikarus_entity_get_properties( + IkarusEntity const * entity, struct IkarusProperty ** properties_out, size_t properties_out_size +); + +/// \brief Gets the value of a property of an entity. +/// \details If the entity has never set the value of the property, the default value is returned (which may be indeterminate). +/// \param entity The entity to get the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to get the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The value of the property or null if the entity does not have the property or an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusEntityValue * get_value(IkarusEntity const * entity, struct IkarusProperty const * property); + +/// \brief Sets the parent folder of an entity. +/// \param entity The entity to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the entity. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the entity in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_entity_set_parent(IkarusEntity * entity, struct IkarusEntityFolder * new_parent, size_t new_position); + +/// \brief Sets the position of an entity within its parent folder. +/// \param entity The entity to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the entity. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_entity_set_position(IkarusEntity * entity, size_t new_position); + +/// \brief Sets the name of an entity. +/// \param entity The entity to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the entity. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_entity_set_name(IkarusEntity * entity, char const * new_name); + +/// \brief Sets the value of a property of an entity. +/// \param entity The entity to set the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to set the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param value The new value of the property. +/// \pre \li Must not be null. +/// \pre \li Must be of the same type as the property. +/// \param validate_settings If set, this function fails not only if the type of the value is invalid, but also if it's not +/// valid under the properties settings. \see property.h +/// \remark If the entity does not have the property, this function fails. +IKA_API void ikarus_entity_set_value( + IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue * value, bool validate_settings +); + +/// \brief Converts an entity to an object. +/// \param entity The entity to convert. +/// \return The constructed object, representing the entity. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity const * entity); + +/// \brief Compares two entities. +/// \param left The left entity to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param right The right entity to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return True if the two entities are equal, false otherwise. +/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two +/// entities reference the same entity in the same project. +IKA_API bool ikarus_entity_is_equal(IkarusEntity const * left, IkarusEntity const * right); IKARUS_END_HEADER diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index ef8c592..256e8a0 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -1,16 +1,9 @@ #pragma once /// \file object.h -/// \author Folling +/// \author Folling -#include -#include -#include #include -#include -#include -#include -#include /// \defgroup object Objects /// \brief Objects are a compound type of all types of objects in the database. @@ -25,96 +18,26 @@ IKARUS_BEGIN_HEADER -struct IkarusFolder; - -/// \private \brief The data of an object. -union IkarusObjectData { - /// \private \brief The blueprint data of the object. - IkarusBlueprint blueprint; - /// \private \brief The property data of the object. - IkarusProperty property; - /// \private \brief The entity data of the object. - IkarusEntity entity; - /// \private \brief The blueprint folder data of the object. - IkarusBlueprintFolder blueprint_folder; - /// \private \brief The property folder data of the object. - IkarusPropertyFolder property_folder; - /// \private \brief The entity folder data of the object. - IkarusEntityFolder entity_folder; -}; - /// \brief A generic object. Wraps all types of objects, including folders. -struct IkarusObject { - /// \private \brief The type of the object. - IkarusObjectType type; - /// \private \brief The data of the object. - IkarusObjectData data; -}; - -/// \brief Constructs an object from a blueprint. -/// \param blueprint The blueprint to construct the object from. -/// \return The constructed object, representing the blueprint. -IKA_API IkarusObject ikarus_object_from_blueprint(IkarusBlueprint blueprint); - -/// \brief Constructs an object from a property. -/// \param property The property to construct the object from. -/// \return The constructed object, representing the property. -IKA_API IkarusObject ikarus_object_from_property(IkarusProperty property); - -/// \brief Constructs an object from an entity. -/// \param entity The entity to construct the object from. -/// \return The constructed object, representing the entity. -IKA_API IkarusObject ikarus_object_from_entity(IkarusEntity entity); - -/// \brief Constructs an object from a blueprint folder. -/// \param blueprint The folder to construct the object from. -/// \return The constructed object, representing the folder. -IKA_API IkarusObject ikarus_object_from_blueprint_folder(IkarusBlueprintFolder folder); - -/// \brief Constructs an object from a property folder. -/// \param property The folder to construct the object from. -/// \return The constructed object, representing the folder. -IKA_API IkarusObject ikarus_object_from_property_folder(IkarusPropertyFolder folder); - -/// \brief Constructs an object from a entity folder. -/// \param entity The folder to construct the object from. -/// \return The constructed object, representing the folder. -IKA_API IkarusObject ikarus_object_from_entity_folder(IkarusEntityFolder folder); - -/// \brief Constructs an object from a folder. -/// \param folder The folder to construct the object from. -/// \return The constructed object, representing the folder. -IKA_API IkarusObject ikarus_object_from_folder(IkarusFolder folder); - -/// \brief Compares two objects for equality. -/// \details Since ids store the type of the object, this boils down to a simple comparison of the ids. -/// \param left The left side of the comparison. -/// \param right The right side of the comparison. -/// \return Whether the objects are equal. -IKA_API bool ikarus_object_is_equal(IkarusObject left, IkarusObject right); - -/// \brief Fetches the type of an object. -/// \param object The object to fetch the type of. -/// \return The type of the object. -IKA_API IkarusObjectType ikarus_object_get_type(IkarusObject object); +struct IkarusObject; /// \brief Visits an object. Calling the appropriate function for the object's type. /// \param object The object to visit. -/// \param blueprint The function to call if the object is a blueprint. -/// \param property The function to call if the object is a property. -/// \param entity The function to call if the object is an entity. -/// \param blueprint_folder The function to call if the object is a blueprint folder. -/// \param property_folder The function to call if the object is a property folder. -/// \param entity_folder The function to call if the object is an entity folder. -/// \param data The data to pass to the functions. +/// \param blueprint_visitor The function to call if the object is a blueprint. Skipped if null. +/// \param property_visitor The function to call if the object is a property. Skipped if null. +/// \param entity_visitor The function to call if the object is an entity. Skipped if null. +/// \param blueprint_folder_visitor The function to call if the object is a blueprint folder. Skipped if null. +/// \param property_folder_visitor The function to call if the object is a property folder. Skipped if null. +/// \param entity_folder_visitor The function to call if the object is an entity folder. Skipped if null. +/// \param data The data passed to the visitor functions. IKA_API void ikarus_object_visit( - IkarusObject object, - void (*visit_blueprint)(IkarusBlueprint, void *), - void (*visit_property)(IkarusProperty, void *), - void (*visit_entity)(IkarusEntity, void *), - void (*visit_blueprint_folder)(IkarusBlueprintFolder, void *), - void (*visit_property_folder)(IkarusPropertyFolder, void *), - void (*visit_entity_folder)(IkarusEntityFolder, void *), + IkarusObject * object, + void (*blueprint_visitor)(struct IkarusBlueprint *, void *), + void (*property_visitor)(struct IkarusProperty *, void *), + void (*entity_visitor)(struct IkarusEntity *, void *), + void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder *, void *), + void (*property_folder_visitor)(struct IkarusPropertyFolder *, void *), + void (*entity_folder_visitor)(struct IkarusEntityFolder *, void *), void * data ); diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 0fafc68..73f724b 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -1,38 +1,36 @@ #pragma once +/// \file object_type.h +/// \author Folling + +#include + +/// \addtogroup objects Objects +/// @{ + +IKARUS_BEGIN_HEADER + // IMPLEMENTATION_DETAIL_OBJECT_TYPES -/// \file object.h -/// \author Folling - -#include -#include - -/// \addtogroup object Objects -/// @{ - /// \brief The type of an object. -/// \remark Folders have the 4th bit set. +/// \remark The folder types are identical to their counterparts in #IkarusFolderType. enum IkarusObjectType { /// \brief Not an object or no object. IkarusObjectType_None = 0, /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 1, + IkarusObjectType_Blueprint = 0b00000001, /// \brief An IkarusProperty. - IkarusObjectType_Property = 2, + IkarusObjectType_Property = 0b00000010, /// \brief An IkarusEntity. - IkarusObjectType_Entity = 3, + IkarusObjectType_Entity = 0b00000011, /// \brief An IkarusBlueprintFolder - IkarusObjectType_BlueprintFolder = IkarusFolderType_BlueprintFolder, + IkarusObjectType_BlueprintFolder = 0b01000001, /// \brief An IkarusPropertyFolder - IkarusObjectType_PropertyFolder = IkarusFolderType_PropertyFolder, + IkarusObjectType_PropertyFolder = 0b01000010, /// \brief An IkarusEntityFolder - IkarusObjectType_EntityFolder = IkarusFolderType_EntityFolder, + IkarusObjectType_EntityFolder = 0b01000011, }; -/// \brief Constructs an IkarusObjectType from an IkarusFolderType. -/// \param type The IkarusFolderType of which to construct the IkarusObjectType from. -/// \return The constructed IkarusObjectType, representing the folder type. -IKA_API IkarusObjectType ikarus_object_type_from_folder_type(IkarusFolderType type); +IKARUS_END_HEADER /// @} diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index d104ac3..edbbdd1 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -1,17 +1,16 @@ #pragma once -// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION, IMPLEMENTATION_DETAIL_PROPERTY_TYPES +// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION /// \file property.h -/// \author Folling +/// \author Folling -#include #include -#include -#include +#include /// \defgroup properties Properties /// \brief Properties define the structure and types of data. +/// @{ IKARUS_BEGIN_HEADER @@ -50,23 +49,181 @@ IKARUS_BEGIN_HEADER /// Fetching the value for some property of some entity will return the property's default value if none is specified. /// This default value is specified when the property is created and can be updated later. /// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. -struct IkarusProperty { - /// \private \brief The id of the property. - IkarusId id; -}; +struct IkarusProperty; -/// \brief The type of a property. -/// \details Designates the type of data stored by the property as well as which settings are -/// available. -/// \see IkarusPropertySettings -enum IkarusPropertyType { - /// \brief A true/false boolean-like value. - IkarusPropertyType_Toggle, - /// \brief An arbitrary numeric value. - IkarusPropertyType_Number, - /// \brief An arbitrary textual value. - IkarusPropertyType_Text, -}; +/// \brief Creates a property +/// \param project The project the property is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property_source The property source the property is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent_folder The parent folder of the property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the property in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param property_info The info of the property. +/// \pre \li Must not be null. +/// \return The created property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusProperty * ikarus_property_create( + struct IkarusProject * project, + struct IkarusPropertySource * property_source, + struct IkarusPropertyFolder * parent_folder, + size_t position, + char const * name, + struct IkarusPropertyTypeInfo * property_info +); + +/// \brief Copies a property. +/// \details Creates a deep copy of the property including all of its settings and associated values. +/// \param property The property to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param source The source to copy the property to. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent_folder The parent folder of the property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the property in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusProperty * ikarus_property_copy( + struct IkarusProperty * property, + struct IkarusPropertySource * source, + struct IkarusPropertyFolder * parent_folder, + size_t position, + char const * name +); + +/// \brief Deletes a property. +/// \param property The property to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The property must not be accessed after deletion. +IKA_API void ikarus_property_delete(struct IkarusProperty * property); + +/// \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. +/// \return The project of the property or null if an error occurs. +IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty const * property); + +/// \brief Gets the parent folder of a property. +/// \param property The property to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the property or null if an error occurs. +IKA_API struct IkarusPropertyFolder * ikarus_property_get_parent(IkarusProperty const * property); + +/// \brief Gets the position of a property within its parent folder. +/// \param property The property to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the property or undefined if an error occurs. +IKA_API size_t ikarus_property_get_position(IkarusProperty const * property); + +/// \brief Gets the name of a property. +/// \param property The property to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the property or null if an error occurs. +/// \remark The returned pointer is valid until the property is freed but may be invalidated by other operations. +IKA_API char const * ikarus_property_get_name(IkarusProperty const * property); + +/// \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. +/// \return The type info of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertyTypeInfo * ikarus_property_get_type_info(IkarusProperty const * property); + +/// \brief Gets the source of a property. +/// \param property The property to get the source of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The source of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertySource * ikarus_property_get_source(IkarusProperty const * property); + +/// \brief Gets the default value of a property. +/// \param property The property to get the default value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The default value of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); + +/// \brief Sets the parent folder of a property. +/// \param property The property to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the property in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_property_set_parent( + IkarusProperty * property, struct IkarusPropertyFolder * new_parent, size_t new_position +); + +/// \brief Sets the position of a property within its parent folder. +/// \param property The property to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the property. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_property_set_position(IkarusProperty * property, size_t new_position); + +/// \brief Sets 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 new_name The new name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_property_set_name(IkarusProperty * property, char const * new_name); + +/// \brief Sets the type info of a property and resets all values to the new default value. +/// \param property The property to set the type info of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_type The new type info of the property. +/// \param attempt_conversion Whether to attempt to convert the property's values to the new type info. Conversion rules are +/// unspecified for now, but follow common sense. +IKA_API void ikarus_property_set_type_info( + IkarusProperty * property, struct IkarusPropertyTypeInfo new_type_info, bool attempt_conversion +); + +/// \brief Converts a property to an object. +/// \param property The property to convert. +/// \return The constructed object, representing the property. +IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty const * property); + +/// \brief Compares two properties. +/// \param left The left property to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param right The right property to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return True if the two properties are equal, false otherwise. +/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two +/// properties reference the same property in the same project. +IKA_API bool ikarus_property_is_equal(IkarusProperty const * left, IkarusProperty const * right); IKARUS_END_HEADER diff --git a/include/ikarus/objects/property_source.h b/include/ikarus/objects/property_source.h new file mode 100644 index 0000000..4dbcd47 --- /dev/null +++ b/include/ikarus/objects/property_source.h @@ -0,0 +1,47 @@ +#pragma once + +/// \file property_source.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// @{ + +IKARUS_BEGIN_HEADER + +struct PropertySource; + +/// \brief Creates an blueprint property source. +/// \param blueprint The blueprint to create the property source for. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The created property source or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct PropertySource * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint); + +/// \brief Creates an entity property source. +/// \param entity The entity to create the property source for. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The created property source or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct PropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity); + +/// \brief Visits a property source, calling the appropriate callback. +/// \param property_source The property source to visit. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprint_visitor The callback to call if the source is a blueprint, skipped if null. +/// \param entity_visitor The callback to call if the source is an entity, skipped if null. +/// \param user_data User data to pass to the callbacks. +IKA_API void ikarus_property_source_visit( + struct PropertySource * property_source, + void (*blueprint_visitor)(struct IkarusBlueprint *, void *), + void (*entity_visitor)(struct IkarusEntity *, void *), + void * user_data +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/property_type.h b/include/ikarus/objects/property_type.h index 3709a37..e26b1f0 100644 --- a/include/ikarus/objects/property_type.h +++ b/include/ikarus/objects/property_type.h @@ -3,29 +3,33 @@ // IMPLEMENTATION_DETAIL_PROPERTY_TYPES /// \file property_type.h -/// \author Folling +/// \author Folling #include -IKARUS_BEGIN_HEADER - -/// \defgroup property_types Property Types -/// \brief Property Types delineate the type of data stored by a property. +/// \addtogroup properties Properties /// @{ +IKARUS_BEGIN_HEADER + /// \brief The type of a property. /// \details Designates the type of data stored by the property as well as which settings are /// available. -/// \see IkarusPropertySettings enum IkarusPropertyType { - /// \brief A true/false boolean-like value. + /// \brief A true/false boolean-esque value. IkarusPropertyType_Toggle, - /// \brief An arbitrary numeric value. + /// \brief A numeric value, limited to IEEE 80 bit floating point numbers. IkarusPropertyType_Number, - /// \brief An arbitrary textual value. + /// \brief An arbitrary UTF-8 textual value. IkarusPropertyType_Text, }; -/// @} +/// \brief Fetches the default value for a property type. +/// \remark Not to be confused with the default value of a property. See ikarus_property_get_default_value +/// \param type The property type. +/// \return The default value for the property type or null if an error occurs. +IKA_API struct IkarusValue * ikarus_property_type_get_default_default_value(IkarusPropertyType type); IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/property_type_info.h b/include/ikarus/objects/property_type_info.h new file mode 100644 index 0000000..cd7b6b9 --- /dev/null +++ b/include/ikarus/objects/property_type_info.h @@ -0,0 +1,79 @@ +#pragma once + +/// \file property_info.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief Information about a property. +/// \details Property information includes their type and settings consolidated to ascertain type safety. +struct IkarusPropertyTypeInfo; + +/// \brief Information about a toggle property. +struct IkarusTogglePropertyInfo; + +/// \brief Information about a number property. +struct IkarusNumberPropertyInfo; + +/// \brief Information about a text property. +struct IkarusTextPropertyInfo; + +/// \brief Creates a new toggle property info. +/// \return The created toggle property info. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTogglePropertyInfo * ikarus_toggle_property_info_create(); +/// \brief Sets the default value of a toggle property info. +/// \param toggle_property_info The toggle property info to set the default value of. +/// \pre \li Must not be null. +/// \param default_value The default value to set. +/// \pre \li Must not be null. +IKA_API void ikarus_toggle_property_info_set_default_value( + IkarusTogglePropertyInfo * toggle_property_info, struct IkarusToggleValue * default_value +); +/// \brief Converts a toggle property info to a generic property info. +/// \param toggle_property_info The toggle property info to convert. +/// \return The converted property info. +IKA_API IkarusPropertyTypeInfo * ikarus_toggle_property_info_to_property_info(IkarusTogglePropertyInfo * toggle_property_info); + +/// \brief Creates a new number property info. +/// \return The created number property info. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberPropertyInfo * ikarus_number_property_info_create(); +/// \brief Sets the default value of a number property info. +/// \param number_property_info The number property info to set the default value of. +/// \pre \li Must not be null. +/// \param default_value The default value to set. +/// \pre \li Must not be null. +IKA_API void ikarus_number_property_info_set_default_value( + IkarusNumberPropertyInfo * number_property_info, struct IkarusNumberValue * default_value +); +/// \brief Converts a number property info to a generic property info. +/// \param number_property_info The number property info to convert. +/// \return The converted property info. +IKA_API IkarusPropertyTypeInfo * ikarus_number_property_info_to_property_info(IkarusNumberPropertyInfo * number_property_info); + +/// \brief Creates a new text property info. +/// \return The created text property info. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextPropertyInfo * ikarus_text_property_info_create(); +/// \brief Sets the default value of a text property info. +/// \param text_property_info The text property info to set the default value of. +/// \pre \li Must not be null. +/// \param default_value The default value to set. +/// \pre \li Must not be null. +IKA_API void ikarus_text_property_info_set_default_value( + IkarusTextPropertyInfo * text_property_info, struct IkarusTextValue * default_value +); +/// \brief Converts a text property info to a generic property info. +/// \param text_property_info The text property info to convert. +/// \return The converted property info. +IKA_API IkarusPropertyTypeInfo * ikarus_text_property_info_to_property_info(IkarusTextPropertyInfo * text_property_info); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/project/project.h b/include/ikarus/project/project.h new file mode 100644 index 0000000..a041946 --- /dev/null +++ b/include/ikarus/project/project.h @@ -0,0 +1,179 @@ +#pragma once + +/// \file project.h +/// \author Folling + +#include +#include + +/// \defgroup projects Projects +/// \brief Projects are the persistence mechanism of Ikarus. Each project contains a set of objects. +/// \details Projects are stored on the filesystem. Each project has a name. +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief An Ikarus project. +/// \details A project may only be open once at a time. Opening a project from two different locations Gets undefined +/// behavior. +struct IkarusProject; + +/// \brief Creates a persisted project on the filesystem. +/// \param path The path to the project. +/// \pre \li Must not be null. +/// \pre \li Must point to a valid unused path on the system. +/// \param name The name of the project. Must neither be null nor empty. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created project or null if an error occurs. +/// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use +/// ikarus_project_delete +IKA_API IkarusProject * ikarus_project_create(char const * path, char const * name); + +/// \brief Creates a project in memory. +/// \param name The name of the project. Must neither be null nor empty. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created project or null if an error occurs. +/// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use +/// ikarus_project_delete +IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name); + +/// \brief Opens an existing project. +/// \param path The path to the project. +/// \pre \li Must not be null. +/// \pre \li Must point to an existing project on the system. +/// \return The opened project or null if an error occurs. +/// \remark Must be freed using ikarus_free. Freeing does not delete the project from the filesystem. For that, use +/// ikarus_project_delete +IKA_API IkarusProject * ikarus_project_open(char const * path); + +/// \brief Copies a project to a new location. +/// \details The new project is not opened. +/// \param project The project to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param target_path The new location of the project. +/// \pre \li Must not be null. +/// \pre \li Must point to a valid unused path on the system. +/// \param target_name The name of the new project. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \remark If successful the project connection remains intact. The previous location will still exist. +IKA_API void ikarus_project_copy(IkarusProject const * project, char const * target_path, char const * target_name); + +/// \brief Deletes a project and all its associated data from the filesystem. +/// \param project The project to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark also frees the project. +/// \remark In-Memory projects will just be freed. +/// \remark If deletion fails, the project pointer remains intact. +IKA_API void ikarus_project_delete(IkarusProject * project); + +/// \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. +/// \return The name of the project. +/// \remark Must be freed using #ikarus_free. +IKA_API char const * ikarus_project_get_name(IkarusProject const * project); + +/// \brief Sets the name of a project. +/// \param project The project to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the project. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_name); + +/// \brief Gets the path of a project. +/// \param project The project to get the path of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The path of the project. +/// \remark Must be freed using #ikarus_free. +IKA_API char const * ikarus_project_get_path(IkarusProject const * project); + +/// \brief Moves a project to a new location. +/// \param project The project to move. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param target_path The new location of the project. +/// \pre \li Must not be null. +/// \pre \li Must point to a valid unused path on the system. +/// \remark If successful the project connection remains intact. The previous location will not exist anymore. +/// \remark Due to the nature of filesystems this function may not be atomic. +IKA_API void ikarus_project_move(IkarusProject * project, char const * target_path); + +/// \brief Gets the error code of a project. +/// \param project The project to get the error code of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The error code of the project. +IKA_API int ikarus_project_get_error_code(IkarusProject const * project); +/// \brief Gets the error message of a project. +/// \param project The project to get the error message of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The error message of the project. +/// \remark The returned pointer is valid until the project is freed but may be altered by other operations. +/// \warning Must not be freed. +IKA_API char const * ikarus_project_get_error_message(IkarusProject const * project); + +/// \brief Gets the blueprint root folder of a project. +/// \param project The project to get the blueprint root folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The blueprint root folder of the project or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder(IkarusProject const * project); + +/// \brief Gets the number of blueprints of a project. +/// \param project The project to get the number of blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of blueprints or undefined if an error occurs. +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project); + +/// \brief Gets the blueprints of a project. +/// \param project The project to get the blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprints_out The buffer to write the blueprints to. +/// \pre \li Must not be null. +/// \param blueprints_out_size The size of the buffer. +IKA_API void ikarus_project_get_blueprints( + IkarusProject const * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size +); + +/// \brief Gets the entity root folder of a project. +/// \param project The project to get the entity root folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The entity root folder of the project or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(IkarusProject const * project); + +/// \brief Gets the number of entities of a project. +/// \param project The project to get the number of entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of entities or undefined if an error occurs. +IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project); + +/// \brief Gets the entities of a project. +/// \param project The project to get the entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param entities_out The buffer to write the entities to. +/// \pre \li Must not be null. +/// \param entities_out_size The size of the buffer. +IKA_API void ikarus_project_get_entities( + IkarusProject const * project, struct IkarusEntity ** entities_out, size_t entities_out_size +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/scopes/blueprint_scope.h b/include/ikarus/scopes/blueprint_scope.h index eac5eac..56fa7ec 100644 --- a/include/ikarus/scopes/blueprint_scope.h +++ b/include/ikarus/scopes/blueprint_scope.h @@ -1,7 +1,7 @@ #pragma once /// \file blueprint_scope.h -/// \author Folling +/// \author Folling #include #include @@ -12,14 +12,18 @@ IKARUS_BEGIN_HEADER /// \brief The global scope of all blueprints. -struct IkarusBlueprintScope { - /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. - short _dummy; -}; +struct IkarusBlueprintScope; /// \brief Creates a blueprint scope. /// \return The created blueprint scope. -IKA_API IkarusBlueprintScope ikarus_blueprint_scope_create(); +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusBlueprintScope * ikarus_blueprint_scope_create(); + +/// \brief Converts a blueprint scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +/// \remark Must be freed with #ikarus_free. +IKA_API struct IkarusObjectScope * ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope); IKARUS_END_HEADER diff --git a/include/ikarus/scopes/entity_scope.h b/include/ikarus/scopes/entity_scope.h index e29d725..e1838cc 100644 --- a/include/ikarus/scopes/entity_scope.h +++ b/include/ikarus/scopes/entity_scope.h @@ -1,9 +1,8 @@ #pragma once /// \file entity_scope.h -/// \author Folling +/// \author Folling -#include #include /// \addtogroup object_scopes ObjectScopes @@ -12,14 +11,18 @@ IKARUS_BEGIN_HEADER /// \brief The global scope of all entities. -struct IkarusEntityScope { - /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. - short _dummy; -}; +struct IkarusEntityScope; -/// \brief Creates a entity scope. +/// \brief Creates an entity scope. /// \return The created entity scope. -IKA_API IkarusEntityScope ikarus_entity_scope_create(); +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusEntityScope * ikarus_entity_scope_create(); + +/// Converts an entity scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +/// \remark Must be freed with #ikarus_free. +IKA_API struct IkarusObjectScope * ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope); IKARUS_END_HEADER diff --git a/include/ikarus/scopes/object_scope.h b/include/ikarus/scopes/object_scope.h index 1c16d9f..69972cf 100644 --- a/include/ikarus/scopes/object_scope.h +++ b/include/ikarus/scopes/object_scope.h @@ -3,12 +3,9 @@ // IMPLEMENTATION_DETAIL_OBJECT_SCOPES /// \file object_scope.h -/// \author Folling +/// \author Folling #include -#include -#include -#include /// \defgroup object_scopes Object Scopes /// \brief Scopes define where objects belong to. @@ -17,65 +14,30 @@ IKARUS_BEGIN_HEADER -/// \private \brief The data for an object scope. -union IkarusObjectScopeData { - /// \private \brief The blueprint data of the scope. - IkarusBlueprintScope _blueprint; - /// \private \brief The property data of the scope. - IkarusPropertyScope _property; - /// \private \brief The entity data of the scope. - IkarusEntityScope _entity; -}; +/// \brief The scope of an object. +struct IkarusObjectScope; -/// The type of an object scope. +/// \brief The type of an object scope. enum IkarusObjectScopeType { /// \brief The scope is a blueprint scope. - IkarusObjectScopeType_Blueprint, + IkarusObjectScopeType_Blueprint = 1, /// \brief The scope is a property scope. - IkarusObjectScopeType_Property, + IkarusObjectScopeType_Property = 2, /// \brief The scope is an entity scope. - IkarusObjectScopeType_Entity + IkarusObjectScopeType_Entity = 3 }; -/// \brief The scope of an object. -struct IkarusObjectScope { - /// \private \brief Represents the type of the scope. - IkarusObjectScopeType _type; - /// \private \brief Represents the data of the scope. - IkarusObjectScopeData _data; -}; - -/// \brief Converts a blueprint scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope scope); - -/// \brief Converts a property scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope scope); - -/// Converts an entity scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope scope); - -/// \brief Fetches the type of an object scope. -/// \param scope The scope to fetch the type of. -/// \return The type of the scope. -IKA_API IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope scope); - /// \brief Visits an object scope, calling the appropriate function. /// \param scope The scope to visit. -/// \param blueprint The function to call if the scope is an #IkarusBlueprintScope. -/// \param property The function to call if the scope is an #IkarusPropertyScope. -/// \param entity The function to call if the scope is an #IkarusEntityScope. +/// \param blueprint_visitor The function to call if the scope is an #IkarusBlueprintScope. +/// \param property_visitor The function to call if the scope is an #IkarusPropertyScope. +/// \param entity_visitor The function to call if the scope is an #IkarusEntityScope. /// \remark function pointers may be null in which case they are not called. IKA_API void ikarus_object_scope_visit( - IkarusObjectScope scope, - void (*blueprint)(IkarusBlueprintScope, void *), - void (*property)(IkarusPropertyScope, void *), - void (*entity)(IkarusEntityScope, void *), + IkarusObjectScope * scope, + void (*blueprint_visitor)(struct IkarusBlueprintScope *, void *), + void (*property_visitor)(struct IkarusPropertyScope *, void *), + void (*entity_visitor)(struct IkarusEntityScope *, void *), void * data ); diff --git a/include/ikarus/scopes/property_scope.h b/include/ikarus/scopes/property_scope.h index 6de365c..d7b38db 100644 --- a/include/ikarus/scopes/property_scope.h +++ b/include/ikarus/scopes/property_scope.h @@ -1,63 +1,46 @@ #pragma once /// \file property_scope.h -/// \author Folling +/// \author Folling #include #include -#include -#include /// \addtogroup object_scopes ObjectScopes /// @{ IKARUS_BEGIN_HEADER -/// \brief Data for a property scope. This can either be a blueprint or an entity. -union IkarusPropertyScopeData { - /// \private \brief The blueprint the property is scoped to. - IkarusBlueprint _blueprint; - /// \private \brief The entity the property is scoped to. - IkarusEntity _entity; -}; - -/// \brief The type of a property scope. This can either be a blueprint or an entity. -enum IkarusPropertyScopeType { - /// \brief The property is scoped to a blueprint. - IkarusPropertyScopeType_Blueprint, - /// \brief The property is scoped to an entity. - IkarusPropertyScopeType_Entity -}; - /// \brief The scope of a property -struct IkarusPropertyScope { - /// \private \brief Represents the type of the scope. - IkarusPropertyScopeType _type; - /// \private \brief Represents the data of the scope. - IkarusPropertyScopeData _data; -}; +struct IkarusPropertyScope; /// \brief Creates a property scope from a blueprint. /// \param blueprint The blueprint the property is scoped to. /// \return The created property scope. -IKA_API IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint blueprint); -/// \brief Creates a property scope from a entity. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusPropertyScope * ikarus_property_scope_create_blueprint(struct IkarusBlueprint * blueprint); +/// \brief Creates a property scope from an entity. /// \param entity The entity the property is scoped to. /// \return The created property scope. -IKA_API IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity entity); +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusPropertyScope * ikarus_property_scope_create_entity(struct IkarusEntity * entity); -/// \brief Fetches the type of an property scope. -/// \param scope The scope to fetch the type of. -/// \return The type of the scope. -IKA_API IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope scope); +/// \brief Converts a property scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +/// \remark Must be freed with #ikarus_free. +IKA_API struct IkarusObjectScope * ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope); /// \brief Visits a property scope, calling the appropriate function. /// \param scope The scope to to visit -/// \param blueprint The function to call if the property is scoped to a blueprint. -/// \param entity The function to call if the property is scoped to an entity. -/// \param data Optional data to pass to the functions. -void ikarus_property_scope_visit( - IkarusPropertyScope scope, void (*blueprint)(IkarusBlueprint, void *), void (*entity)(IkarusEntity, void *), void * data +/// \param blueprint_visitor The function to call if the property is scoped to a blueprint. +/// \param entity_visitor The function to call if the property is scoped to an entity. +/// \param data The data passed to the visitor functions. +IKA_API void ikarus_property_scope_visit( + IkarusPropertyScope * scope, + void (*blueprint_visitor)(struct IkarusBlueprint *, void *), + void (*entity_visitor)(struct IkarusEntity *, void *), + void * data ); IKARUS_END_HEADER diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h index d5f7ab2..6fa7e95 100644 --- a/include/ikarus/stdtypes.h +++ b/include/ikarus/stdtypes.h @@ -3,6 +3,7 @@ #ifdef __cplusplus #include #include +// NOLINTNEXTLINE(google-global-names-in-headers) using std::size_t; #else #include diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index e1457af..a30cf2d 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -3,10 +3,10 @@ // IMPLEMENTATION_DETAIL_PROPERTY_TYPES /// \file value.h -/// \author Folling +/// \author Folling #include -#include +#include #include IKARUS_BEGIN_HEADER @@ -15,149 +15,115 @@ IKARUS_BEGIN_HEADER /// \brief The values stored in entities. /// \details Each entity has a value for each property it is associated with. /// The value is of the type specified by the property and constrained by the property's settings. +/// A value may be indeterminate which means it is unknown or not specified. /// \see PropertyType PropertySettings /// @{ /// \brief A true/false boolean-like value. For example "IsDead". -struct IkarusToggleValue { - /// \private \brief The value of the property. - bool _value; -}; +struct IkarusToggleValue; /// \brief An arbitrary numeric value. For example "Age". -struct IkarusNumberValue { - /// \private \brief The value of the property. - long double _value; -}; +struct IkarusNumberValue; /// \brief An arbitrary textual value. For example "First Name". -struct IkarusTextValue { - /// \private \brief The value of the property. - char const * _value; -}; - -/// \private \brief The data for a value. -union IkarusEntityValueData { - /// \private \brief The value as a toggle. - IkarusToggleValue toggle; - /// \private \brief The value as a number. - IkarusNumberValue number; - /// \private \brief The value as text. - IkarusTextValue text; -}; - -/// \brief The state of an entity value. -/// \details States provide insight into the nature of a value. -enum IkarusEntityValueState { - /// \brief The value is invalid. - IkarusEntityValueState_Invalid, - /// \brief The value is normal and can be used as-is. - IkarusEntityValueState_Normal, - /// \brief The value is unknown. - IkarusEntityValueState_Indeterminate, -}; +struct IkarusTextValue; /// \brief The value of an entity associated with a property. -struct IkarusEntityValue { - /// \private \brief The type of the value. - IkarusPropertyType _type; - /// \private \brief The data for the value.p - IkarusEntityValueData _data; - /// \private \brief The state of the value. - IkarusEntityValueState _state; -}; +struct IkarusEntityValue; -/// \brief Creates an entity value from a toggle value. +/// \brief Creates a toggle value from a boolean. /// \param value The toggle value. /// \return The entity value. -IKA_API IkarusEntityValue ikarus_value_create_toggle(bool value); -/// \brief Creates an entity value from a number value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value); +/// \brief Creates an indeterminate toggle value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); +/// \brief Sets the value of a toggle value. +/// \param value The toggle value. +/// \pre Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); + +/// \brief Creates a number value from a number. /// \param value The number value. +/// \pre Must be finite & not NaN. /// \return The entity value. -/// \remark If the value is NaN or infinity an InvalidEntityValue is returned. -IKA_API IkarusEntityValue ikarus_value_create_number(long double value); -/// \brief Creates an entity value from a text value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberValue * ikarus_number_value_create(long double value); +/// \brief Creates an indeterminate number value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); +/// \brief Sets the value of a number value. +/// \param value The number value. +/// \pre Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); + +/// \brief Creates a text value from string. /// \param value The text value. +/// \pre Must not be null. /// \return The entity value. -/// \remark If the value is null an InvalidEntityValue is returned. -IKA_API IkarusEntityValue ikarus_value_create_text(char const * value); - -/// \brief Creates an indeterminate entity value of a given type. -/// \param type The type of the value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextValue * ikarus_text_value_create(char const * value); +/// \brief Creates an indeterminate text value. /// \return The entity value. -IKA_API IkarusEntityValue ikarus_value_create_indeterminate(IkarusPropertyType type); +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); +/// \brief Sets the value of a text value. +/// \param value The text value. +/// \pre Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); -/// \brief Fetches the default value for a property type. -/// \remark Not to be confused with the default value of a property. See ikarus_property_get_default_value -/// \param type The property type. -/// \return The default value for the property type. -IKA_API IkarusEntityValue ikarus_value_get_default(IkarusPropertyType type); +/// \brief Checks if a toggle value is indeterminate. +/// \param value The toggle value. +/// \pre Must not be null. +/// \return True if the value is indeterminate, false otherwise. +IKA_API bool ikarus_toggle_value_is_indeterminate(IkarusToggleValue const * value); +/// \brief Checks if a number value is indeterminate. +/// \param value The number value. +/// \pre Must not be null. +/// \return True if the value is indeterminate, false otherwise. +IKA_API bool ikarus_number_value_is_indeterminate(IkarusNumberValue const * value); +/// \brief Checks if a text value is indeterminate. +/// \param value The text value. +/// \pre Must not be null. +/// \return True if the value is indeterminate, false otherwise. +IKA_API bool ikarus_text_value_is_indeterminate(IkarusTextValue const * value); /// \brief Fetches the underlying value of a toggle value. /// \param value The toggle value. /// \return The underlying value. +/// \warning If the value is indeterminate, false is returned. IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value); /// \brief Fetches the underlying value of a number value. /// \param value The number value. /// \return The underlying value. +/// \warning If the value is indeterminate, 0.0 is returned. IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value); /// \brief Fetches the underlying value of a text value. /// \param value The text value. -/// \return The underlying value. +/// \return A copy of the underlying value. +/// \remark The returned value is a copy and owned by the caller. +/// \warning If the value is indeterminate, an empty string is returned. IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); -/// \brief Checks if a toggle value is equal to a boolean. -/// \param value The toggle value. -/// \param check The boolean value. -/// \return False if value is null. True if it is equal to check, false otherwise. -IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * value, bool check); - -/// \brief Checks if a number value is equal to a number. -/// \param value The number value. -/// \param check The number value. -/// \return False if value is null. True if it is equal to check, false otherwise. -IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * value, long double check); - -/// \brief Checks if a text value is equal to a string. -/// \param value The text value. -/// \param check The string value. -/// \return False if value or check are null. True if it is equal to check, false otherwise. -IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * value, char const * check); - -/// \brief Checks if two entity values are equal. -/// \details Two entity values are equal if they are of the same type and their value is considered equal. -/// Note that floating point values can only be checked for approximate equality. -/// \param left The left-hand entity value. -/// \param right The right-hand entity value. -/// \return True if the values are considered equal, false otherwise. -/// \remark Null values compare false to all other values. As do invalid values. Indeterminate values however, compare true to -/// other indeterminate values of the same type. -IKA_API bool ikarus_value_is_equal(IkarusEntityValue const * left, IkarusEntityValue const * right); - -/// \brief Checks if an entity value is invalid. -/// \param value The entity value. -/// \return True if the value is invalid or null, false otherwise. -IKA_API bool ikarus_value_is_invalid(IkarusEntityValue const * value); - -/// \brief Fetches the type of an entity value. -/// \param value The entity value. -/// \return The type of the entity value. -IKA_API IkarusPropertyType ikarus_value_get_type(IkarusEntityValue const * value); - /// \brief Visits an entity value, calling the appropriate function for the value's type. /// \param value The entity value to visit. -/// \param toggle The function to call if the value is a toggle value. -/// \param number The function to call if the value is a number value. -/// \param text The function to call if the value is a text value. -/// \param data The data to pass to the functions. -/// \remark function pointers may be null in which case they are not called. +/// \param toggle The function to call if the value is a toggle value. Skipped if null. +/// \param number The function to call if the value is a number value. Skipped if null. +/// \param text The function to call if the value is a text value. Skipped if null. +/// \param data The data passed to the visitor functions. IKA_API void ikarus_value_visit( - IkarusEntityValue const * value, - void (*toggle)(IkarusToggleValue const *, void *), - void (*number)(IkarusNumberValue const *, void *), - void (*text)(IkarusTextValue const *, void *), + IkarusEntityValue * value, + void (*toggle)(IkarusToggleValue *, void *), + void (*number)(IkarusNumberValue *, void *), + void (*text)(IkarusTextValue *, void *), void * data ); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a0665aa..6b00545 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ file( GLOB_RECURSE FILES + "*.hpp" "*.cpp" ) diff --git a/src/folders/blueprint_folder.hpp b/src/folders/blueprint_folder.hpp new file mode 100644 index 0000000..c98b51b --- /dev/null +++ b/src/folders/blueprint_folder.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct IkarusBlueprintFolder { + IkarusId id; + std::string name_buffer; +}; diff --git a/src/folders/entity_folder.hpp b/src/folders/entity_folder.hpp new file mode 100644 index 0000000..c69e5c7 --- /dev/null +++ b/src/folders/entity_folder.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct IkarusEntityFolder { + IkarusId id; + std::string name_buffer; +}; diff --git a/src/folders/folder.hpp b/src/folders/folder.hpp new file mode 100644 index 0000000..9589a5d --- /dev/null +++ b/src/folders/folder.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +struct IkarusEntityFolder { + std::variant data; +}; diff --git a/src/folders/property_folder.hpp b/src/folders/property_folder.hpp new file mode 100644 index 0000000..11aef0c --- /dev/null +++ b/src/folders/property_folder.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct IkarusPropertyFolder { + IkarusId id; + std::string name_buffer; +}; diff --git a/src/id.cpp b/src/id.cpp index caa08be..b1d28fe 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -4,28 +4,28 @@ #include -IkarusId ikarus_id_from_data(int64_t data) { - return IkarusId{.data = data}; +uint64_t const IKARUS_ID_OBJECT_TYPE_BITS = 8; +uint64_t const IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; + +auto ikarus_id_get_object_type(IkarusId id) -> IkarusObjectType { + return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); } -IkarusObjectType ikarus_id_get_object_type(IkarusId id) { - return static_cast(id.data >> 56); +auto ikarus_id_is_equal(IkarusId left, IkarusId right) -> bool { + return left == right; } -bool ikarus_id_is_equal(IkarusId left, IkarusId right) { - return left.data == right.data; -} - -bool ikarus_id_is_none(IkarusId id) { +auto ikarus_id_is_none(IkarusId id) -> bool { return ikarus_id_is_equal(id, IKARUS_ID_NONE); } -bool ikarus_id_is_unspecified(IkarusId id) { +auto ikarus_id_is_unspecified(IkarusId id) -> bool { return ikarus_id_is_equal(id, IKARUS_ID_UNSPECIFIED); } TEST_CASE("id_object_type", "[id]") { - auto id = ikarus_id_from_data(static_cast(IkarusObjectType_Blueprint) << 56); + // NOLINTNEXTLINE(readability-magic-numbers) + auto id = static_cast(IkarusObjectType_Blueprint) << IKARUS_ID_OBJECT_RANDOM_BITS; REQUIRE(ikarus_id_get_object_type(id) == IkarusObjectType_Blueprint); REQUIRE(!ikarus_id_is_none(id) == IkarusObjectType_Blueprint); @@ -33,9 +33,9 @@ TEST_CASE("id_object_type", "[id]") { } TEST_CASE("id_equal", "[id]") { - auto id = ikarus_id_from_data(static_cast(IkarusObjectType_Blueprint) << 56); + auto id = static_cast(IkarusObjectType_Blueprint) << IKARUS_ID_OBJECT_RANDOM_BITS; auto copy = id; - auto third = ikarus_id_from_data(static_cast(IkarusObjectType_Property) << 56); + auto third = static_cast(IkarusObjectType_Property) << IKARUS_ID_OBJECT_RANDOM_BITS; REQUIRE(ikarus_id_is_equal(id, copy)); REQUIRE(!ikarus_id_is_equal(id, third)); diff --git a/src/id.hpp b/src/id.hpp new file mode 100644 index 0000000..4499c17 --- /dev/null +++ b/src/id.hpp @@ -0,0 +1,40 @@ +// IMPLEMENTATION_DETAIL_DATABASE + +/// \file id.h +/// \author Folling + +/// \privatesection + +#pragma once + +#include +#include +#include + +IKARUS_BEGIN_HEADER + +/// \defgroup id Ids +/// \brief Ids are used to identify objects in the database. +/// \details They are stored as 64 bit integers with the following layout: +/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. +/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. +/// - next 7 bits: #IkarusObjectType +/// - last 56 bits: incremented counter generated by the database +/// @{ + +/// \brief A wrapper around a 64 bit integer that represents the id of an object. +/// \details They are stored as 64 bit integers with the following layout: +/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. +/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. +/// - next 7 bits: #IkarusObjectType +/// - last 56 bits: incremented counter generated by the database +using IkarusId = int64_t; + +/// \brief Fetches the object type of the given id. +/// \param id The id to fetch the object type for. +/// \return The object type of the given id. +IKA_API IkarusObjectType ikarus_id_get_object_type(IkarusId id); + +/// @} + +IKARUS_END_HEADER diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp new file mode 100644 index 0000000..f938b8f --- /dev/null +++ b/src/objects/blueprint.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +/// \private +struct IkarusBlueprint { + IkarusId id; +}; diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp new file mode 100644 index 0000000..a348533 --- /dev/null +++ b/src/objects/entity.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +/// \private +struct IkarusEntity { + IkarusId id; +}; diff --git a/src/objects/object.cpp b/src/objects/object.cpp index 7378c39..fe405b9 100644 --- a/src/objects/object.cpp +++ b/src/objects/object.cpp @@ -1,13 +1,11 @@ #include "ikarus/objects/object.h" -#include - #include -#include +#include -IkarusObjectType ikarus_object_get_type(IkarusObject object) { - return object.type; +IkarusObjectType ikarus_object_get_type(IkarusObject const * object) { + return object->type; } TEST_CASE("object_type", "[object]") { @@ -22,6 +20,6 @@ TEST_CASE("object_type", "[object]") { for (auto type : types) { auto object = IkarusObject{.type = type}; - REQUIRE(ikarus_object_get_type(object) == type); + REQUIRE(ikarus_object_get_type(&object) == type); } } diff --git a/src/objects/object.hpp b/src/objects/object.hpp new file mode 100644 index 0000000..0d696aa --- /dev/null +++ b/src/objects/object.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +struct IkarusObject { + std::variant + data; +}; diff --git a/src/objects/object_type.cpp b/src/objects/object_type.cpp deleted file mode 100644 index a9a3e32..0000000 --- a/src/objects/object_type.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "ikarus/objects/object_type.h" - -#include - -#include - -IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type) { - return static_cast(type); -} - -TEST_CASE("folder_to_object_type_conversion", "[object_type]") { - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_None) == IkarusObjectType_None); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_BlueprintFolder) == IkarusObjectType_BlueprintFolder); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_PropertyFolder) == IkarusObjectType_PropertyFolder); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_EntityFolder) == IkarusObjectType_EntityFolder); -} diff --git a/src/objects/property.hpp b/src/objects/property.hpp new file mode 100644 index 0000000..f72ad8c --- /dev/null +++ b/src/objects/property.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +/// \private +struct IkarusProperty { + IkarusId id; +}; diff --git a/src/projects/project.hpp b/src/projects/project.hpp new file mode 100644 index 0000000..c805935 --- /dev/null +++ b/src/projects/project.hpp @@ -0,0 +1,10 @@ +#include "ikarus/project/project.h" + +#include +#include + +/// \private +struct IkarusProject { + std::string name; + std::filesystem::path path; +}; diff --git a/src/scopes/blueprint_scope.cpp b/src/scopes/blueprint_scope.cpp new file mode 100644 index 0000000..c1eb0fc --- /dev/null +++ b/src/scopes/blueprint_scope.cpp @@ -0,0 +1,12 @@ +#include "ikarus/scopes/blueprint_scope.h" + +#include +#include + +IkarusBlueprintScope * ikarus_blueprint_scope_create() { + return new IkarusBlueprintScope{}; +} + +struct IkarusObjectScope * ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope) { + return new IkarusObjectScope{.data = *scope}; +} diff --git a/src/scopes/blueprint_scope.hpp b/src/scopes/blueprint_scope.hpp new file mode 100644 index 0000000..ba0bc7d --- /dev/null +++ b/src/scopes/blueprint_scope.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include + +struct IkarusBlueprintScope {}; diff --git a/src/scopes/entity_scope.cpp b/src/scopes/entity_scope.cpp new file mode 100644 index 0000000..a0f78d0 --- /dev/null +++ b/src/scopes/entity_scope.cpp @@ -0,0 +1,12 @@ +#include "ikarus/scopes/entity_scope.h" + +#include +#include + +IkarusEntityScope * ikarus_entity_scope_create() { + return new IkarusEntityScope{}; +} + +IkarusObjectScope * ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope) { + return new IkarusObjectScope{.data = *scope}; +} diff --git a/src/scopes/entity_scope.hpp b/src/scopes/entity_scope.hpp new file mode 100644 index 0000000..6df6550 --- /dev/null +++ b/src/scopes/entity_scope.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include + +struct IkarusEntityScope {}; diff --git a/src/scopes/object_scope.cpp b/src/scopes/object_scope.cpp index 348b007..ddae9a9 100644 --- a/src/scopes/object_scope.cpp +++ b/src/scopes/object_scope.cpp @@ -2,194 +2,81 @@ #include #include +#include #include -#include -#include +#include + #include #include #include -IkarusBlueprintScope ikarus_blueprint_scope_create() { - return IkarusBlueprintScope{._dummy = 0}; -} +#include -IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope scope) { - IkarusObjectScopeData data{}; - data._blueprint = scope; - - return IkarusObjectScope{._type = IkarusObjectScopeType_Blueprint, ._data = data}; -} - -IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint blueprint) { - IkarusPropertyScopeData data{}; - data._blueprint = blueprint; - return IkarusPropertyScope{._type = IkarusPropertyScopeType_Blueprint, ._data = data}; -} - -IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity entity) { - IkarusPropertyScopeData data{}; - data._entity = entity; - return IkarusPropertyScope{._type = IkarusPropertyScopeType_Entity, ._data = data}; -} - -IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope scope) { - IkarusObjectScopeData data{}; - data._property = scope; - - return IkarusObjectScope{._type = IkarusObjectScopeType_Property, ._data = data}; -} - -IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope scope) { - return scope._type; -} - -void ikarus_property_scope_visit( - IkarusPropertyScope scope, void(blueprint)(IkarusBlueprint, void *), void(entity)(IkarusEntity, void *), void * data -) { - switch (scope._type) { - case IkarusPropertyScopeType_Blueprint: blueprint(scope._data._blueprint, data); break; - case IkarusPropertyScopeType_Entity: entity(scope._data._entity, data); break; - } -} - -IkarusEntityScope ikarus_entity_scope_create() { - return IkarusEntityScope{._dummy = 0}; -} - -IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope scope) { - IkarusObjectScopeData data{}; - data._entity = scope; - - return IkarusObjectScope{._type = IkarusObjectScopeType_Entity, ._data = data}; -} - -IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope scope) { - return scope._type; +IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope * scope) { + return std::visit( + cppbase::overloaded{ + []([[maybe_unused]] IkarusBlueprintScope const& scope) { return IkarusObjectScopeType_Blueprint; }, + []([[maybe_unused]] IkarusPropertyScope const& scope) { return IkarusObjectScopeType_Property; }, + []([[maybe_unused]] IkarusEntityScope const& scope) { return IkarusObjectScopeType_Entity; }}, + scope->data + ); } void ikarus_object_scope_visit( - IkarusObjectScope scope, - void(blueprint)(IkarusBlueprintScope, void *), - void(property)(IkarusPropertyScope, void *), - void(entity)(IkarusEntityScope, void *), + IkarusObjectScope * scope, + void(blueprint_visitor)(IkarusBlueprintScope *, void *), + void(property_visitor)(IkarusPropertyScope *, void *), + void(entity_visitor)(IkarusEntityScope *, void *), void * data ) { - switch (scope._type) { - case IkarusObjectScopeType_Blueprint: { - if (blueprint != nullptr) { - blueprint(scope._data._blueprint, data); - } - break; - } - case IkarusObjectScopeType_Property: { - if (property != nullptr) { - property(scope._data._property, data); - } - break; - } - case IkarusObjectScopeType_Entity: { - if (entity != nullptr) { - entity(scope._data._entity, data); - } - break; - } - } + std::visit( + cppbase::overloaded{ + [blueprint_visitor, data](IkarusBlueprintScope& scope) { blueprint_visitor(&scope, data); }, + [property_visitor, data](IkarusPropertyScope& scope) { property_visitor(&scope, data); }, + [entity_visitor, data](IkarusEntityScope& scope) { entity_visitor(&scope, data); }}, + scope->data + ); } TEST_CASE("blueprint_object_scope_conversion", "[object_scope]") { - auto blueprint_scope = ikarus_blueprint_scope_create(); - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); - REQUIRE(blueprint_object_scope._type == IkarusObjectScopeType_Blueprint); -} - -TEST_CASE("property_scope_type", "[object_scope]") { - auto blueprint = IkarusBlueprint{}; - auto entity = IkarusEntity{}; - - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); - auto property_entity_scope = ikarus_property_scope_create_entity(entity); - - REQUIRE(ikarus_property_scope_get_type(property_blueprint_scope) == IkarusPropertyScopeType_Blueprint); - REQUIRE(ikarus_property_scope_get_type(property_entity_scope) == IkarusPropertyScopeType_Entity); + auto * blueprint_scope = ikarus_blueprint_scope_create(); + auto * blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); + REQUIRE(ikarus_object_scope_get_type(blueprint_object_scope) == IkarusObjectScopeType_Blueprint); } TEST_CASE("property_object_scope_conversion", "[object_scope]") { - auto blueprint = IkarusBlueprint{}; - auto entity = IkarusEntity{}; + auto * blueprint = ikarus_blueprint_create(); + auto * entity = ikarus_entity_create(); - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); - auto property_blueprint_object_scope = ikarus_property_scope_to_object_scope(property_blueprint_scope); + auto * property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); + auto * property_blueprint_object_scope = ikarus_property_scope_to_object_scope(property_blueprint_scope); - REQUIRE(property_blueprint_object_scope._type == IkarusObjectScopeType_Property); + REQUIRE(ikarus_object_scope_get_type(property_blueprint_object_scope) == IkarusObjectScopeType_Property); - auto property_entity_scope = ikarus_property_scope_create_entity(entity); - auto property_entity_object_scope = ikarus_property_scope_to_object_scope(property_entity_scope); + auto * property_entity_scope = ikarus_property_scope_create_entity(entity); + auto * property_entity_object_scope = ikarus_property_scope_to_object_scope(property_entity_scope); - REQUIRE(property_entity_object_scope._type == IkarusObjectScopeType_Property); -} - -TEST_CASE("property_scope_visiting", "[object_scope]") { - auto blueprint = IkarusBlueprint{}; - auto entity = IkarusEntity{}; - - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); - auto property_entity_scope = ikarus_property_scope_create_entity(entity); - - int test = 0; - - ikarus_property_scope_visit( - property_blueprint_scope, - [](IkarusBlueprint, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusEntity, void * data) { *reinterpret_cast(data) = 2; }, - &test - ); - - REQUIRE(test == 1); - - ikarus_property_scope_visit( - property_entity_scope, - [](IkarusBlueprint, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusEntity, void * data) { *reinterpret_cast(data) = 2; }, - &test - ); - - REQUIRE(test == 2); + REQUIRE(ikarus_object_scope_get_type(property_entity_object_scope) == IkarusObjectScopeType_Property); } TEST_CASE("entity_object_scope_conversion", "[object_scope]") { - auto entity_scope = ikarus_entity_scope_create(); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); - REQUIRE(entity_object_scope._type == IkarusObjectScopeType_Entity); -} - -TEST_CASE("object_scope_type_fetching", "[object_scope]") { - auto blueprint = IkarusBlueprint{}; - - auto blueprint_scope = ikarus_blueprint_scope_create(); - auto property_scope = ikarus_property_scope_create_blueprint(blueprint); - auto entity_scope = ikarus_entity_scope_create(); - - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); - auto property_object_scope = ikarus_property_scope_to_object_scope(property_scope); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); - - REQUIRE(ikarus_object_scope_get_type(blueprint_object_scope) == IkarusObjectScopeType_Blueprint); - REQUIRE(ikarus_object_scope_get_type(property_object_scope) == IkarusObjectScopeType_Property); + auto * entity_scope = ikarus_entity_scope_create(); + auto * entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); REQUIRE(ikarus_object_scope_get_type(entity_object_scope) == IkarusObjectScopeType_Entity); } TEST_CASE("object_scope_visiting", "[object_scope]") { - auto blueprint = IkarusBlueprint{}; + auto * blueprint = ikarus_blueprint_create(); - auto blueprint_scope = ikarus_blueprint_scope_create(); - auto property_scope = ikarus_property_scope_create_blueprint(blueprint); - auto entity_scope = ikarus_entity_scope_create(); + auto * blueprint_scope = ikarus_blueprint_scope_create(); + auto * property_scope = ikarus_property_scope_create_blueprint(blueprint); + auto * entity_scope = ikarus_entity_scope_create(); - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); - auto property_object_scope = ikarus_property_scope_to_object_scope(property_scope); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); + auto * blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); + auto * property_object_scope = ikarus_property_scope_to_object_scope(property_scope); + auto * entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); auto scopes = { std::make_pair(blueprint_object_scope, 1), @@ -202,9 +89,9 @@ TEST_CASE("object_scope_visiting", "[object_scope]") { ikarus_object_scope_visit( scope, - [](IkarusBlueprintScope, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusPropertyScope, void * data) { *reinterpret_cast(data) = 2; }, - [](IkarusEntityScope, void * data) { *reinterpret_cast(data) = 3; }, + [](IkarusBlueprintScope *, void * data) { *static_cast(data) = 1; }, + [](IkarusPropertyScope *, void * data) { *static_cast(data) = 2; }, + [](IkarusEntityScope *, void * data) { *static_cast(data) = 3; }, &test ); diff --git a/src/scopes/object_scope.hpp b/src/scopes/object_scope.hpp new file mode 100644 index 0000000..9bc3b09 --- /dev/null +++ b/src/scopes/object_scope.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include +#include +#include + +struct IkarusObjectScope { + std::variant data; +}; diff --git a/src/scopes/property_scope.cpp b/src/scopes/property_scope.cpp new file mode 100644 index 0000000..26f45f5 --- /dev/null +++ b/src/scopes/property_scope.cpp @@ -0,0 +1,35 @@ +#include "ikarus/scopes/property_scope.h" + +#include + +#include +#include +#include +#include + +IkarusPropertyScope * ikarus_property_scope_create_blueprint(IkarusBlueprint * blueprint) { + return new IkarusPropertyScope{.data = *blueprint}; +} + +IkarusPropertyScope * ikarus_property_scope_create_entity(IkarusEntity * entity) { + return new IkarusPropertyScope{.data = *entity}; +} + +IkarusObjectScope * ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope) { + return new IkarusObjectScope{.data = *scope}; +} + +void ikarus_property_scope_visit( + IkarusPropertyScope * scope, + void (*blueprint_func)(IkarusBlueprint *, void *), + void (*entity_func)(IkarusEntity *, void *), + void * data +) { + std::visit( + cppbase::overloaded{ + [blueprint_func, data](IkarusBlueprint& blueprint) { blueprint_func(&blueprint, data); }, + [entity_func, data](IkarusEntity& entity) { entity_func(&entity, data); }, + }, + scope->data + ); +} diff --git a/src/scopes/property_scope.hpp b/src/scopes/property_scope.hpp new file mode 100644 index 0000000..4d3aad0 --- /dev/null +++ b/src/scopes/property_scope.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include + +#include +#include + +struct IkarusPropertyScope { + std::variant data; +}; diff --git a/src/values/value.cpp b/src/values/value.cpp index 56fa9c5..a5d6457 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -25,7 +25,7 @@ IKA_API IkarusEntityValue value_create_invalid(IkarusPropertyType type) { IkarusEntityValue ikarus_value_create_toggle(bool value) { return IkarusEntityValue{ ._type = IkarusPropertyType_Toggle, - ._data = IkarusEntityValueData{.toggle = IkarusToggleValue{._value = value}}, + ._data = IkarusEntityValueData{._toggle = IkarusToggleValue{._value = value}}, ._state = IkarusEntityValueState_Normal, }; } @@ -37,7 +37,7 @@ IkarusEntityValue ikarus_value_create_number(long double value) { return IkarusEntityValue{ ._type = IkarusPropertyType_Number, - ._data = IkarusEntityValueData{.number = IkarusNumberValue{._value = value}}, + ._data = IkarusEntityValueData{._number = IkarusNumberValue{._value = value}}, ._state = IkarusEntityValueState_Normal, }; } @@ -49,7 +49,7 @@ IkarusEntityValue ikarus_value_create_text(char const * value) { return IkarusEntityValue{ ._type = IkarusPropertyType_Text, - ._data = IkarusEntityValueData{.text = IkarusTextValue{._value = value}}, + ._data = IkarusEntityValueData{._text = IkarusTextValue{._value = value}}, ._state = IkarusEntityValueState_Normal, }; } @@ -59,15 +59,15 @@ IkarusEntityValue ikarus_value_create_indeterminate(IkarusPropertyType type) { switch (type) { case IkarusPropertyType_Toggle: { - data.toggle = IkarusToggleValue{._value = false}; + data._toggle = IkarusToggleValue{._value = false}; break; } case IkarusPropertyType_Number: { - data.number = IkarusNumberValue{._value = 0.0}; + data._number = IkarusNumberValue{._value = 0.0}; break; } case IkarusPropertyType_Text: { - data.text = IkarusTextValue{._value = ""}; + data._text = IkarusTextValue{._value = ""}; break; } default: return value_create_invalid(type); @@ -133,9 +133,9 @@ bool ikarus_value_is_equal(IkarusEntityValue const * left, IkarusEntityValue con } switch (left->_type) { - case IkarusPropertyType_Toggle: return left->_data.toggle._value == right->_data.toggle._value; - case IkarusPropertyType_Number: return left->_data.number._value == right->_data.number._value; - case IkarusPropertyType_Text: return std::strcmp(left->_data.text._value, right->_data.text._value) == 0; + case IkarusPropertyType_Toggle: return left->_data._toggle._value == right->_data._toggle._value; + case IkarusPropertyType_Number: return left->_data._number._value == right->_data._number._value; + case IkarusPropertyType_Text: return std::strcmp(left->_data._text._value, right->_data._text._value) == 0; default: return false; } } @@ -162,19 +162,19 @@ void ikarus_value_visit( switch (value->_type) { case IkarusPropertyType_Toggle: { if (toggle != nullptr) { - toggle(&value->_data.toggle, data); + toggle(&value->_data._toggle, data); } break; } case IkarusPropertyType_Number: { if (number != nullptr) { - number(&value->_data.number, data); + number(&value->_data._number, data); } break; } case IkarusPropertyType_Text: { if (text != nullptr) { - text(&value->_data.text, data); + text(&value->_data._text, data); } break; } @@ -186,14 +186,14 @@ TEST_CASE("toggle_value_creation", "[value]") { auto toggle_value = ikarus_value_create_toggle(true); REQUIRE(ikarus_value_get_type(&toggle_value) == IkarusPropertyType_Toggle); - REQUIRE(ikarus_toggle_value_is_equal(&toggle_value._data.toggle, true)); + REQUIRE(ikarus_toggle_value_is_equal(&toggle_value._data._toggle, true)); } TEST_CASE("number_value_creation", "[value]") { auto number_value = ikarus_value_create_number(1.0); REQUIRE(ikarus_value_get_type(&number_value) == IkarusPropertyType_Number); - REQUIRE(ikarus_number_value_is_equal(&number_value._data.number, 1.0)); + REQUIRE(ikarus_number_value_is_equal(&number_value._data._number, 1.0)); auto nan_value = ikarus_value_create_number(std::numeric_limits::quiet_NaN()); REQUIRE(ikarus_value_is_invalid(&nan_value)); @@ -209,7 +209,7 @@ TEST_CASE("text_value_creation", "[value]") { auto text_value = ikarus_value_create_text("test"); REQUIRE(ikarus_value_get_type(&text_value) == IkarusPropertyType_Text); - REQUIRE(ikarus_text_value_is_equal(&text_value._data.text, "test")); + REQUIRE(ikarus_text_value_is_equal(&text_value._data._text, "test")); auto null_value = ikarus_value_create_text(nullptr); REQUIRE(ikarus_value_is_invalid(&null_value)); @@ -232,8 +232,8 @@ TEST_CASE("toggle_value_underlying", "[value]") { auto true_toggle_value = ikarus_value_create_toggle(true); auto false_toggle_value = ikarus_value_create_toggle(false); - REQUIRE(ikarus_toggle_value_get_underlying(&true_toggle_value._data.toggle) == true); - REQUIRE(ikarus_toggle_value_get_underlying(&false_toggle_value._data.toggle) == false); + REQUIRE(ikarus_toggle_value_get_underlying(&true_toggle_value._data._toggle) == true); + REQUIRE(ikarus_toggle_value_get_underlying(&false_toggle_value._data._toggle) == false); } TEST_CASE("number_value_underlying", "[value]") { @@ -241,25 +241,25 @@ TEST_CASE("number_value_underlying", "[value]") { auto third_number_value = ikarus_value_create_number(1.0 / 3.0); auto large_number_value = ikarus_value_create_number(1.2345678910e123); - REQUIRE(ikarus_number_value_get_underlying(&zero_number_value._data.number) == 0.0); - REQUIRE(ikarus_number_value_get_underlying(&third_number_value._data.number) == 1.0 / 3.0); - REQUIRE(ikarus_number_value_get_underlying(&large_number_value._data.number) == 1.2345678910e123); + REQUIRE(ikarus_number_value_get_underlying(&zero_number_value._data._number) == 0.0); + REQUIRE(ikarus_number_value_get_underlying(&third_number_value._data._number) == 1.0 / 3.0); + REQUIRE(ikarus_number_value_get_underlying(&large_number_value._data._number) == 1.2345678910e123); } TEST_CASE("text_value_underlying", "[value]") { auto test_value = ikarus_value_create_text("test"); auto empty_value = ikarus_value_create_text(""); - REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&test_value._data.text), "test") == 0); - REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&empty_value._data.text), "") == 0); + REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&test_value._data._text), "test") == 0); + REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&empty_value._data._text), "") == 0); } TEST_CASE("toggle_comparison", "[value]") { auto true_toggle_value = ikarus_value_create_toggle(true); auto false_toggle_value = ikarus_value_create_toggle(false); - REQUIRE(ikarus_toggle_value_is_equal(&true_toggle_value._data.toggle, true)); - REQUIRE(ikarus_toggle_value_is_equal(&false_toggle_value._data.toggle, false)); + REQUIRE(ikarus_toggle_value_is_equal(&true_toggle_value._data._toggle, true)); + REQUIRE(ikarus_toggle_value_is_equal(&false_toggle_value._data._toggle, false)); } TEST_CASE("number_comparison", "[value]") { @@ -267,17 +267,17 @@ TEST_CASE("number_comparison", "[value]") { auto third_number_value = ikarus_value_create_number(1.0 / 3.0); auto large_number_value = ikarus_value_create_number(1.2345678910e123); - REQUIRE(ikarus_number_value_is_equal(&zero_number_value._data.number, 0.0)); - REQUIRE(ikarus_number_value_is_equal(&third_number_value._data.number, 1.0 / 6.0 + 1.0 / 6.0)); - REQUIRE(ikarus_number_value_is_equal(&large_number_value._data.number, 1.2345678910e123)); + REQUIRE(ikarus_number_value_is_equal(&zero_number_value._data._number, 0.0)); + REQUIRE(ikarus_number_value_is_equal(&third_number_value._data._number, 1.0 / 6.0 + 1.0 / 6.0)); + REQUIRE(ikarus_number_value_is_equal(&large_number_value._data._number, 1.2345678910e123)); } TEST_CASE("text_comparison", "[value]") { auto test_value = ikarus_value_create_text("test"); auto empty_value = ikarus_value_create_text(""); - REQUIRE(ikarus_text_value_is_equal(&test_value._data.text, "test")); - REQUIRE(ikarus_text_value_is_equal(&empty_value._data.text, "")); + REQUIRE(ikarus_text_value_is_equal(&test_value._data._text, "test")); + REQUIRE(ikarus_text_value_is_equal(&empty_value._data._text, "")); } TEST_CASE("value_comparison", "[value]") { diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 416e326..a397a5e 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 416e326c60f3763a43dec8c66e58616ab50d1a2a +Subproject commit a397a5e8c35c7b30ecac8b4994301a585e24560b From 3608794bf0a523e9c6593c144f775da08c6959ac Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 14 Nov 2023 16:26:29 +0100 Subject: [PATCH 008/166] intermediate commit Signed-off-by: Folling --- include/ikarus/folders/blueprint_folder.h | 151 -------- include/ikarus/folders/blueprint_tree_item.h | 31 -- include/ikarus/folders/entity_folder.h | 151 -------- include/ikarus/folders/entity_tree_item.h | 31 -- include/ikarus/folders/folder.h | 38 -- include/ikarus/folders/folder_type.h | 26 -- include/ikarus/folders/property_folder.h | 159 -------- include/ikarus/folders/property_tree_item.h | 31 -- include/ikarus/objects/blueprint.h | 78 +--- include/ikarus/objects/entity.h | 7 +- include/ikarus/objects/property.h | 61 +--- .../ikarus/{project => persistence}/project.h | 0 include/ikarus/scopes/blueprint_scope.h | 30 -- include/ikarus/scopes/entity_scope.h | 29 -- include/ikarus/scopes/object_scope.h | 46 --- include/ikarus/scopes/property_scope.h | 48 --- src/folders/blueprint_folder.hpp | 8 - src/folders/entity_folder.hpp | 8 - src/folders/folder.hpp | 13 - src/folders/property_folder.hpp | 8 - src/id.cpp | 26 +- src/objects/blueprint.hpp | 3 +- src/objects/entity.hpp | 3 +- src/objects/object.cpp | 25 -- src/objects/object.hpp | 10 +- src/objects/property.hpp | 3 +- .../migrations/m1_initial_layout.sql | 111 ++++++ src/{projects => persistence}/project.hpp | 5 +- src/scopes/blueprint_scope.cpp | 12 - src/scopes/blueprint_scope.hpp | 5 - src/scopes/entity_scope.cpp | 12 - src/scopes/entity_scope.hpp | 5 - src/scopes/object_scope.cpp | 100 ----- src/scopes/object_scope.hpp | 11 - src/scopes/property_scope.cpp | 35 -- src/scopes/property_scope.hpp | 12 - src/values/value.cpp | 343 ------------------ 37 files changed, 131 insertions(+), 1544 deletions(-) delete mode 100644 include/ikarus/folders/blueprint_folder.h delete mode 100644 include/ikarus/folders/blueprint_tree_item.h delete mode 100644 include/ikarus/folders/entity_folder.h delete mode 100644 include/ikarus/folders/entity_tree_item.h delete mode 100644 include/ikarus/folders/folder.h delete mode 100644 include/ikarus/folders/folder_type.h delete mode 100644 include/ikarus/folders/property_folder.h delete mode 100644 include/ikarus/folders/property_tree_item.h rename include/ikarus/{project => persistence}/project.h (100%) delete mode 100644 include/ikarus/scopes/blueprint_scope.h delete mode 100644 include/ikarus/scopes/entity_scope.h delete mode 100644 include/ikarus/scopes/object_scope.h delete mode 100644 include/ikarus/scopes/property_scope.h delete mode 100644 src/folders/blueprint_folder.hpp delete mode 100644 src/folders/entity_folder.hpp delete mode 100644 src/folders/folder.hpp delete mode 100644 src/folders/property_folder.hpp delete mode 100644 src/objects/object.cpp create mode 100644 src/persistence/migrations/m1_initial_layout.sql rename src/{projects => persistence}/project.hpp (61%) delete mode 100644 src/scopes/blueprint_scope.cpp delete mode 100644 src/scopes/blueprint_scope.hpp delete mode 100644 src/scopes/entity_scope.cpp delete mode 100644 src/scopes/entity_scope.hpp delete mode 100644 src/scopes/object_scope.cpp delete mode 100644 src/scopes/object_scope.hpp delete mode 100644 src/scopes/property_scope.cpp delete mode 100644 src/scopes/property_scope.hpp delete mode 100644 src/values/value.cpp diff --git a/include/ikarus/folders/blueprint_folder.h b/include/ikarus/folders/blueprint_folder.h deleted file mode 100644 index 430c147..0000000 --- a/include/ikarus/folders/blueprint_folder.h +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once - -/// \file blueprint_folder.h -/// \author Folling - -#include - -/// \addtogroup blueprints Blueprints -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief A blueprint folder, storing blueprints and other blueprint folders. -struct IkarusBlueprintFolder; - -/// \brief Creates a blueprint folder. -/// \param project The project the blueprint folder is part of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the blueprint folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created blueprint folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprintFolder * ikarus_blueprint_folder_create( - struct IkarusProject * project, IkarusBlueprintFolder * parent, size_t position, char const * name -); - -/// \brief Copies a blueprint folder. -/// \details Creates a copy of the blueprint folder without its children. -/// \param blueprint_folder The blueprint folder to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the blueprint folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created blueprint folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprintFolder * ikarus_blueprint_folder_copy( - IkarusBlueprintFolder * blueprint_folder, IkarusBlueprintFolder * parent, size_t position, char const * name -); - -/// \brief Deletes a blueprint folder and all its children -/// \param blueprint_folder The blueprint folder to delete. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param keep_children If true, the children of the blueprint folder will be moved to the parent folder. -IKA_API void ikarus_blueprint_folder_delete(IkarusBlueprintFolder * blueprint_folder, bool keep_children); - -/// \brief Gets the project of a blueprint folder. -/// \param blueprint_folder The blueprint folder to get the project of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The project of the blueprint folder or null if an error occurs. -IKA_API struct IkarusProject * ikarus_blueprint_folder_get_project(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Gets the parent folder of a blueprint folder. -/// \param blueprint_folder The blueprint folder to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the blueprint folder or null if an error occurs. -IKA_API struct IkarusBlueprintFolder * ikarus_blueprint_folder_get_parent(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Gets the position of a blueprint folder within its parent folder. -/// \param blueprint_folder The blueprint folder to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the blueprint folder or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_folder_get_position(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Gets the name of a blueprint folder. -/// \param blueprint_folder The blueprint folder to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the blueprint folder or null if an error occurs. -/// \remark The returned pointer is valid until the blueprint folder is freed but may be invalidated by other operations. -IKA_API char const * ikarus_blueprint_folder_get_name(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Gets the number of children of a blueprint folder. -/// \param blueprint_folder The blueprint folder to get the number of children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of children or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_folder_get_child_count(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Gets the children of a blueprint folder. -/// \param blueprint_folder The blueprint folder to get the children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param children_out The buffer to write the children to. -/// \pre \li Must not be null. -/// \param children_out_size The size of the buffer. -IKA_API void ikarus_blueprint_folder_get_children( - IkarusBlueprintFolder const * blueprint_folder, struct IkarusBlueprintTreeItem ** children_out, size_t children_out_size -); - -/// \brief Sets the parent folder of an blueprint folder. -/// \param blueprint_folder The blueprint folder to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the blueprint folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_blueprint_folder_set_parent( - IkarusBlueprintFolder * blueprint_folder, struct IkarusEntityFolder * new_parent, size_t new_position -); - -/// \brief Sets the position of an blueprint folder within its parent folder. -/// \param blueprint_folder The blueprint folder to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the blueprint folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_blueprint_folder_set_position(IkarusBlueprintFolder * blueprint_folder, size_t new_position); - -/// \brief Sets the name of an blueprint folder. -/// \param blueprint_folder The blueprint folder to set the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_name The new name of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_blueprint_folder_set_name(IkarusBlueprintFolder * blueprint_folder, char const * new_name); - -/// \brief Converts a blueprint folder to a generic folder. -/// \param blueprint_folder The blueprint folder to convert. -/// \return The constructed folder, representing the blueprint folder. -IKA_API struct IkarusFolder * ikarus_blueprint_folder_to_folder(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Converts a blueprint folder to an object. -/// \param blueprint_folder The blueprint folder to convert. -/// \return The constructed object, representing the blueprint folder. -IKA_API struct IkarusObject * ikarus_blueprint_folder_to_object(IkarusBlueprintFolder const * blueprint_folder); - -IKARUS_END_HEADER - -// @} diff --git a/include/ikarus/folders/blueprint_tree_item.h b/include/ikarus/folders/blueprint_tree_item.h deleted file mode 100644 index 5a09dcc..0000000 --- a/include/ikarus/folders/blueprint_tree_item.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file blueprint_tree_item.h -/// \author Folling - -#include - -/// \addtogroup blueprints Blueprints -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusBlueprintTreeItem; - -/// \brief Visits a blueprint tree item, calling the appropriate visitor function. -/// \param item The item to visit. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param blueprint_visitor The visitor function called if the item is a blueprint. Skipped if null. -/// \param blueprint_folder_visitor The visitor function called if the item is a blueprint folder. Skipped if null. -/// \param data The data passed to the visitor functions. -IKA_API void ikarus_blueprint_tree_item_visit( - struct IkarusBlueprintTreeItem * item, - void (*blueprint_visitor)(struct IkarusBlueprint * blueprint, void * data), - void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder * folder, void * data), - void * data -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/folders/entity_folder.h b/include/ikarus/folders/entity_folder.h deleted file mode 100644 index 8c516cb..0000000 --- a/include/ikarus/folders/entity_folder.h +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once - -/// \file entity_folder.h -/// \author Folling - -#include - -/// \addtogroup entities Entities -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief A entity folder, storing entities and other entity folders. -struct IkarusEntityFolder; - -/// \brief Creates a entity folder. -/// \param project The project the entity folder is part of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the entity folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created entity folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusEntityFolder * ikarus_entity_folder_create( - struct IkarusProject * project, IkarusEntityFolder * parent, size_t position, char const * name -); - -/// \brief Copies a entity folder. -/// \details Creates a copy of the entity folder without its children. -/// \param entity_folder The entity folder to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the entity folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created entity folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusEntityFolder * ikarus_entity_folder_copy( - IkarusEntityFolder * entity_folder, IkarusEntityFolder * parent, size_t position, char const * name -); - -/// \brief Deletes a entity folder and all its children -/// \param entity_folder The entity folder to delete. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param keep_children If true, the children of the entity folder will be moved to the parent folder. -IKA_API void ikarus_entity_folder_delete(IkarusEntityFolder * entity_folder, bool keep_children); - -/// \brief Gets the project of a entity folder. -/// \param entity_folder The entity folder to get the project of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The project of the entity folder or null if an error occurs. -IKA_API struct IkarusProject * ikarus_entity_folder_get_project(IkarusEntityFolder const * entity_folder); - -/// \brief Gets the parent folder of a entity folder. -/// \param entity_folder The entity folder to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the entity folder or null if an error occurs. -IKA_API struct IkarusEntityFolder * ikarus_entity_folder_get_parent(IkarusEntityFolder const * entity_folder); - -/// \brief Gets the position of a entity folder within its parent folder. -/// \param entity_folder The entity folder to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the entity folder or undefined if an error occurs. -IKA_API size_t ikarus_entity_folder_get_position(IkarusEntityFolder const * entity_folder); - -/// \brief Gets the name of a entity folder. -/// \param entity_folder The entity folder to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the entity folder or null if an error occurs. -/// \remark The returned pointer is valid until the entity folder is freed but may be invalidated by other operations. -IKA_API char const * ikarus_entity_folder_get_name(IkarusEntityFolder const * entity_folder); - -/// \brief Gets the number of children of a entity folder. -/// \param entity_folder The entity folder to get the number of children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of children or undefined if an error occurs. -IKA_API size_t ikarus_entity_folder_get_child_count(IkarusEntityFolder const * entity_folder); - -/// \brief Gets the children of a entity folder. -/// \param entity_folder The entity folder to get the children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param children_out The buffer to write the children to. -/// \pre \li Must not be null. -/// \param children_out_size The size of the buffer. -IKA_API void ikarus_entity_folder_get_children( - IkarusEntityFolder const * entity_folder, struct IkarusEntityTreeItem ** children_out, size_t children_out_size -); - -/// \brief Sets the parent folder of an entity folder. -/// \param entity_folder The entity folder to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the entity folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_entity_folder_set_parent( - IkarusEntityFolder * entity_folder, struct IkarusEntityFolder * new_parent, size_t new_position -); - -/// \brief Sets the position of an entity folder within its parent folder. -/// \param entity_folder The entity folder to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the entity folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_entity_folder_set_position(IkarusEntityFolder * entity_folder, size_t new_position); - -/// \brief Sets the name of an entity folder. -/// \param entity_folder The entity folder to set the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_name The new name of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_entity_folder_set_name(IkarusEntityFolder * entity_folder, char const * new_name); - -/// \brief Converts a entity folder to a generic folder. -/// \param entity_folder The entity folder to convert. -/// \return The constructed folder, representing the entity folder. -IKA_API struct IkarusFolder * ikarus_entity_folder_to_folder(IkarusEntityFolder const * entity_folder); - -/// \brief Converts a entity folder to an object. -/// \param entity_folder The entity folder to convert. -/// \return The constructed object, representing the entity folder. -IKA_API struct IkarusObject * ikarus_entity_folder_to_object(IkarusEntityFolder const * entity_folder); - -IKARUS_END_HEADER - -// @} diff --git a/include/ikarus/folders/entity_tree_item.h b/include/ikarus/folders/entity_tree_item.h deleted file mode 100644 index 516455f..0000000 --- a/include/ikarus/folders/entity_tree_item.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file entity_tree_item.h -/// \author Folling - -#include - -/// \addtogroup entities Entities -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusEntityTreeItem; - -/// \brief Visits a entity tree item, calling the appropriate visitor function. -/// \param item The item to visit. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param entity_visitor The visitor function called if the item is a entity. Skipped if null. -/// \param entity_folder_visitor The visitor function called if the item is a entity folder. Skipped if null. -/// \param data The data passed to the visitor functions. -IKA_API void ikarus_entity_tree_item_visit( - struct IkarusEntityTreeItem * item, - void (*entity_visitor)(struct IkarusEntity * entity, void * data), - void (*entity_folder_visitor)(struct IkarusEntityFolder * folder, void * data), - void * data -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/folders/folder.h b/include/ikarus/folders/folder.h deleted file mode 100644 index d0d60e2..0000000 --- a/include/ikarus/folders/folder.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -/// \file folder.h -/// \author Folling - -#include -#include -#include -#include -#include - -IKARUS_BEGIN_HEADER - -/// \defgroup folder Folders -/// \brief Folders are used to group objects together. -/// @{ - -/// \brief A generic folder. Similar to how Objects wrap all types of objects, Folders wrap all types of folders. -struct IkarusFolder; - -/// \brief Special value for inserting objects at the end of a folder. -enum FolderPosition { FolderPosition_EndOfFolder = -1 }; - -/// \brief Visits a folder. Calling the appropriate function for the folder's type. -/// \param folder The folder to visit. -/// \param blueprint_visitor The function to call if the folder is a blueprint folder. -/// \param property_visitor The function to call if the folder is a property folder. -/// \param entity_visitor The function to call if the folder is an entity folder. -/// \param data The data passed to the visitor functions. -IKA_API void ikarus_folder_visit( - IkarusFolder * folder, - void (*blueprint_visitor)(IkarusBlueprintFolder *, void *), - void (*property_visitor)(IkarusPropertyFolder *, void *), - void (*entity_visitor)(IkarusEntityFolder *, void *), - void * data -); - -IKARUS_END_HEADER diff --git a/include/ikarus/folders/folder_type.h b/include/ikarus/folders/folder_type.h deleted file mode 100644 index d3ac05e..0000000 --- a/include/ikarus/folders/folder_type.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -// IMPLEMENTATION_DETAIL_FOLDER_TYPES - -/// \file folder_type.h -/// \author Folling - -#include - -/// \addtogroup folder folders -/// @{ - -/// \brief The type of an folder. -/// \remark The values are identical to their counterparts in #IkarusObjectType. -enum IkarusFolderType { - /// \brief Not a folder or no folder. - IkarusFolderType_None = 0, - /// \brief An IkarusBlueprintFolder - IkarusFolderType_BlueprintFolder = 0b0100'0001, - /// \brief An IkarusPropertyFolder - IkarusFolderType_PropertyFolder = 0b0100'0010, - /// \brief An IkarusEntityFolder - IkarusFolderType_EntityFolder = 0b0100'0011, -}; - -/// @} diff --git a/include/ikarus/folders/property_folder.h b/include/ikarus/folders/property_folder.h deleted file mode 100644 index d55d6cf..0000000 --- a/include/ikarus/folders/property_folder.h +++ /dev/null @@ -1,159 +0,0 @@ -#pragma once - -/// \file property_folder.h -/// \author Folling - -#include - -/// \addtogroup properties Properties -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief A property folder, storing properties and other property folders. -struct IkarusPropertyFolder; - -/// \brief Creates a property folder. -/// \param project The project the property folder is part of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the property folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created property folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusPropertyFolder * ikarus_property_folder_create( - struct IkarusProject * project, IkarusPropertyFolder * parent, size_t position, char const * name -); - -/// \brief Copies a property folder. -/// \details Creates a copy of the property folder without its children. -/// \param property_folder The property folder to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the property folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created property folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusPropertyFolder * ikarus_property_folder_copy( - IkarusPropertyFolder * property_folder, IkarusPropertyFolder * parent, size_t position, char const * name -); - -/// \brief Deletes a property folder and all its children -/// \param property_folder The property folder to delete. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param keep_children If true, the children of the property folder will be moved to the parent folder. -IKA_API void ikarus_property_folder_delete(IkarusPropertyFolder * property_folder, bool keep_children); - -/// \brief Gets the project of a property folder. -/// \param property_folder The property folder to get the project of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The project of the property folder or null if an error occurs. -IKA_API struct IkarusProject * ikarus_property_folder_get_project(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the property source of a property folder. -/// \param property_folder The property folder to get the property source of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The property source of the property folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * ikarus_property_folder_get_source(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the parent folder of a property folder. -/// \param property_folder The property folder to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the property folder or null if an error occurs. -IKA_API struct IkarusPropertyFolder * ikarus_property_folder_get_parent(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the position of a property folder within its parent folder. -/// \param property_folder The property folder to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the property folder or undefined if an error occurs. -IKA_API size_t ikarus_property_folder_get_position(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the name of a property folder. -/// \param property_folder The property folder to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the property folder or null if an error occurs. -/// \remark The returned pointer is valid until the property folder is freed but may be invalidated by other operations. -IKA_API char const * ikarus_property_folder_get_name(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the number of children of a property folder. -/// \param property_folder The property folder to get the number of children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of children or undefined if an error occurs. -IKA_API size_t ikarus_property_folder_get_child_count(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the children of a property folder. -/// \param property_folder The property folder to get the children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param children_out The buffer to write the children to. -/// \pre \li Must not be null. -/// \param children_out_size The size of the buffer. -IKA_API void ikarus_property_folder_get_children( - IkarusPropertyFolder const * property_folder, struct IkarusPropertyTreeItem ** children_out, size_t children_out_size -); - -/// \brief Sets the parent folder of an property folder. -/// \param property_folder The property folder to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the property folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_property_folder_set_parent( - IkarusPropertyFolder * property_folder, struct IkarusPropertyFolder * new_parent, size_t new_position -); - -/// \brief Sets the position of an property folder within its parent folder. -/// \param property_folder The property folder to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the property folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_property_folder_set_position(IkarusPropertyFolder * property_folder, size_t new_position); - -/// \brief Sets the name of an property folder. -/// \param property_folder The property folder to set the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_name The new name of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_property_folder_set_name(IkarusPropertyFolder * property_folder, char const * new_name); - -/// \brief Converts a property folder to a generic folder. -/// \param property_folder The property folder to convert. -/// \return The constructed folder, representing the property folder. -IKA_API struct IkarusFolder * ikarus_property_folder_to_folder(IkarusPropertyFolder const * property_folder); - -/// \brief Converts a property folder to an object. -/// \param property_folder The property folder to convert. -/// \return The constructed object, representing the property folder. -IKA_API struct IkarusObject * ikarus_property_folder_to_object(IkarusPropertyFolder const * property_folder); - -IKARUS_END_HEADER - -// @} diff --git a/include/ikarus/folders/property_tree_item.h b/include/ikarus/folders/property_tree_item.h deleted file mode 100644 index 0b37c2c..0000000 --- a/include/ikarus/folders/property_tree_item.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file property_tree_item.h -/// \author Folling - -#include - -/// \addtogroup properties Properties -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusPropertyTreeItem; - -/// \brief Visits a property tree item, calling the appropriate visitor function. -/// \param item The item to visit. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param property_visitor The visitor function called if the item is a property. Skipped if null. -/// \param property_folder_visitor The visitor function called if the item is a property folder. Skipped if null. -/// \param data The data passed to the visitor functions. -IKA_API void ikarus_property_tree_item_visit( - struct IkarusPropertyTreeItem * item, - void (*property_visitor)(struct IkarusProperty * property, void * data), - void (*property_folder_visitor)(struct IkarusPropertyFolder * folder, void * data), - void * data -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index e47b1df..ed35c89 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -21,37 +21,25 @@ struct IkarusBlueprint; /// \param project The project the blueprint is part of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param parent The parent folder of the blueprint. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the blueprint in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the blueprint. /// \pre \li Must not be null. /// \pre \li Must not be empty. /// \return The created blueprint or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprint * ikarus_blueprint_create( - struct IkarusProject * project, struct IkarusBlueprintFolder * parent, size_t position, char const * name -); +IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name); /// \brief Creates a blueprint from an entity. /// \details The created blueprint will have the same properties as the entity. /// \param entity The entity to create the blueprint from. /// \pre \li Must not be null. /// \param link_entity If true, the entity will be linked to the blueprint. If not they will remain separate. -/// \param parent The parent folder of the blueprint. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the blueprint in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the blueprint. Must not be empty. /// \pre \li Must not be null. /// \pre \li Must not be empty. /// \return The created blueprint or null if an error occurs. /// \remark Must be freed using #ikarus_free. IKA_API IkarusBlueprint * ikarus_blueprint_create_from_entity( - struct IkarusEntity * entity, bool link_entity, struct IkarusBlueprintFolder * parent, size_t position, char const * name + struct IkarusEntity * entity, bool link_entity, char const * name ); /// \brief Copies a blueprint. @@ -59,20 +47,13 @@ IKA_API IkarusBlueprint * ikarus_blueprint_create_from_entity( /// \param blueprint The blueprint to copy. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param parent The parent folder of the blueprint. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the blueprint in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the blueprint. /// \pre \li Must not be null. /// \pre \li Must not be empty. /// \return The created blueprint or null if an error occurs. /// \remark Linked entities won't be copied. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprint * ikarus_blueprint_copy( - IkarusBlueprint const * blueprint, struct IkarusBlueprintFolder * parent, size_t position, char const * name -); +IKA_API IkarusBlueprint * ikarus_blueprint_copy(IkarusBlueprint const * blueprint, char const * name); /// \brief Deletes a blueprint. /// \param blueprint The blueprint to delete. @@ -88,20 +69,6 @@ IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); /// \return The project of the blueprint or null if an error occurs. IKA_API struct IkarusProject * ikarus_blueprint_get_project(IkarusBlueprint const * blueprint); -/// \brief Gets the parent folder of a blueprint. -/// \param blueprint The blueprint to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the blueprint or null if an error occurs. -IKA_API struct IkarusBlueprintFolder * ikarus_blueprint_get_parent(IkarusBlueprint const * blueprint); - -/// \brief Gets the position of a blueprint within its parent folder. -/// \param blueprint The blueprint to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the blueprint or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_position(IkarusBlueprint const * blueprint); - /// \brief Gets the name of a blueprint. /// \param blueprint The blueprint to get the name of. /// \pre \li Must not be null. @@ -110,14 +77,6 @@ IKA_API size_t ikarus_blueprint_get_position(IkarusBlueprint const * blueprint); /// \remark The returned pointer is valid until the blueprint is freed but may be invalidated by other operations. IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint); -/// \brief Gets the property root folder of a blueprint. -/// \param blueprint The blueprint to get the root folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The root folder of all properties of the blueprint or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyFolder * ikarus_blueprint_get_property_root_folder(IkarusBlueprint const * blueprint); - /// \brief Gets the number of properties of a blueprint. /// \param blueprint The blueprint to get the number of properties of. /// \pre \li Must not be null. @@ -154,29 +113,6 @@ IKA_API void ikarus_blueprint_get_linked_entities( IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size ); -/// \brief Sets the parent folder of a blueprint. -/// \param blueprint The blueprint to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the blueprint. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the blueprint in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_blueprint_set_parent( - IkarusBlueprint * blueprint, struct IkarusBlueprintFolder * new_parent, size_t new_position -); - -/// \brief Sets the position of a blueprint within its parent folder. -/// \param blueprint The blueprint to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the blueprint. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_blueprint_set_position(IkarusBlueprint * blueprint, size_t new_position); - /// \brief Sets the name of a blueprint. /// \param blueprint The blueprint to set the name of. /// \pre \li Must not be null. @@ -186,14 +122,6 @@ IKA_API void ikarus_blueprint_set_position(IkarusBlueprint * blueprint, size_t n /// \pre \li Must not be empty. IKA_API void ikarus_blueprint_set_name(IkarusBlueprint * blueprint, char const * new_name); -/// \brief Converts a blueprint to an object. -/// \param blueprint The blueprint to convert. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The constructed object, representing the blueprint. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint const * blueprint); - /// \brief Compares two blueprints. /// \param left The left blueprint to compare. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index a24cf79..e73e18d 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,6 +1,7 @@ #pragma once #include +#include /// \file entity.h /// \author Folling @@ -230,12 +231,6 @@ IKA_API void ikarus_entity_set_value( IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue * value, bool validate_settings ); -/// \brief Converts an entity to an object. -/// \param entity The entity to convert. -/// \return The constructed object, representing the entity. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity const * entity); - /// \brief Compares two entities. /// \param left The left entity to compare. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index edbbdd1..10258df 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -7,6 +7,7 @@ #include #include +#include /// \defgroup properties Properties /// \brief Properties define the structure and types of data. @@ -58,11 +59,6 @@ struct IkarusProperty; /// \param property_source The property source the property is part of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param parent_folder The parent folder of the property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the property in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the property. /// \pre \li Must not be null. /// \pre \li Must not be empty. @@ -73,8 +69,6 @@ struct IkarusProperty; IKA_API struct IkarusProperty * ikarus_property_create( struct IkarusProject * project, struct IkarusPropertySource * property_source, - struct IkarusPropertyFolder * parent_folder, - size_t position, char const * name, struct IkarusPropertyTypeInfo * property_info ); @@ -87,22 +81,13 @@ IKA_API struct IkarusProperty * ikarus_property_create( /// \param source The source to copy the property to. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param parent_folder The parent folder of the property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the property in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the property. /// \pre \li Must not be null. /// \pre \li Must not be empty. /// \return The created property or null if an error occurs. /// \remark Must be freed using #ikarus_free. IKA_API struct IkarusProperty * ikarus_property_copy( - struct IkarusProperty * property, - struct IkarusPropertySource * source, - struct IkarusPropertyFolder * parent_folder, - size_t position, - char const * name + struct IkarusProperty * property, struct IkarusPropertySource * source, char const * name ); /// \brief Deletes a property. @@ -119,20 +104,6 @@ IKA_API void ikarus_property_delete(struct IkarusProperty * property); /// \return The project of the property or null if an error occurs. IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty const * property); -/// \brief Gets the parent folder of a property. -/// \param property The property to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the property or null if an error occurs. -IKA_API struct IkarusPropertyFolder * ikarus_property_get_parent(IkarusProperty const * property); - -/// \brief Gets the position of a property within its parent folder. -/// \param property The property to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the property or undefined if an error occurs. -IKA_API size_t ikarus_property_get_position(IkarusProperty const * property); - /// \brief Gets the name of a property. /// \param property The property to get the name of. /// \pre \li Must not be null. @@ -165,29 +136,6 @@ IKA_API struct IkarusPropertySource * ikarus_property_get_source(IkarusProperty /// \remark Must be freed using #ikarus_free. IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); -/// \brief Sets the parent folder of a property. -/// \param property The property to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the property in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_property_set_parent( - IkarusProperty * property, struct IkarusPropertyFolder * new_parent, size_t new_position -); - -/// \brief Sets the position of a property within its parent folder. -/// \param property The property to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the property. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_property_set_position(IkarusProperty * property, size_t new_position); - /// \brief Sets the name of a property. /// \param property The property to set the name of. /// \pre \li Must not be null. @@ -208,11 +156,6 @@ IKA_API void ikarus_property_set_type_info( IkarusProperty * property, struct IkarusPropertyTypeInfo new_type_info, bool attempt_conversion ); -/// \brief Converts a property to an object. -/// \param property The property to convert. -/// \return The constructed object, representing the property. -IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty const * property); - /// \brief Compares two properties. /// \param left The left property to compare. /// \pre \li Must not be null. diff --git a/include/ikarus/project/project.h b/include/ikarus/persistence/project.h similarity index 100% rename from include/ikarus/project/project.h rename to include/ikarus/persistence/project.h diff --git a/include/ikarus/scopes/blueprint_scope.h b/include/ikarus/scopes/blueprint_scope.h deleted file mode 100644 index 56fa7ec..0000000 --- a/include/ikarus/scopes/blueprint_scope.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -/// \file blueprint_scope.h -/// \author Folling - -#include -#include - -/// \addtogroup object_scopes ObjectScopes -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The global scope of all blueprints. -struct IkarusBlueprintScope; - -/// \brief Creates a blueprint scope. -/// \return The created blueprint scope. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusBlueprintScope * ikarus_blueprint_scope_create(); - -/// \brief Converts a blueprint scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -/// \remark Must be freed with #ikarus_free. -IKA_API struct IkarusObjectScope * ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope); - -IKARUS_END_HEADER - -// @} diff --git a/include/ikarus/scopes/entity_scope.h b/include/ikarus/scopes/entity_scope.h deleted file mode 100644 index e1838cc..0000000 --- a/include/ikarus/scopes/entity_scope.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/// \file entity_scope.h -/// \author Folling - -#include - -/// \addtogroup object_scopes ObjectScopes -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The global scope of all entities. -struct IkarusEntityScope; - -/// \brief Creates an entity scope. -/// \return The created entity scope. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusEntityScope * ikarus_entity_scope_create(); - -/// Converts an entity scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -/// \remark Must be freed with #ikarus_free. -IKA_API struct IkarusObjectScope * ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope); - -IKARUS_END_HEADER - -// @} diff --git a/include/ikarus/scopes/object_scope.h b/include/ikarus/scopes/object_scope.h deleted file mode 100644 index 69972cf..0000000 --- a/include/ikarus/scopes/object_scope.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -// IMPLEMENTATION_DETAIL_OBJECT_SCOPES - -/// \file object_scope.h -/// \author Folling - -#include - -/// \defgroup object_scopes Object Scopes -/// \brief Scopes define where objects belong to. -/// \details They are required to differentiate between different types of objects with NULL as their parent. -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The scope of an object. -struct IkarusObjectScope; - -/// \brief The type of an object scope. -enum IkarusObjectScopeType { - /// \brief The scope is a blueprint scope. - IkarusObjectScopeType_Blueprint = 1, - /// \brief The scope is a property scope. - IkarusObjectScopeType_Property = 2, - /// \brief The scope is an entity scope. - IkarusObjectScopeType_Entity = 3 -}; - -/// \brief Visits an object scope, calling the appropriate function. -/// \param scope The scope to visit. -/// \param blueprint_visitor The function to call if the scope is an #IkarusBlueprintScope. -/// \param property_visitor The function to call if the scope is an #IkarusPropertyScope. -/// \param entity_visitor The function to call if the scope is an #IkarusEntityScope. -/// \remark function pointers may be null in which case they are not called. -IKA_API void ikarus_object_scope_visit( - IkarusObjectScope * scope, - void (*blueprint_visitor)(struct IkarusBlueprintScope *, void *), - void (*property_visitor)(struct IkarusPropertyScope *, void *), - void (*entity_visitor)(struct IkarusEntityScope *, void *), - void * data -); - -/// @} - -IKARUS_END_HEADER diff --git a/include/ikarus/scopes/property_scope.h b/include/ikarus/scopes/property_scope.h deleted file mode 100644 index d7b38db..0000000 --- a/include/ikarus/scopes/property_scope.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -/// \file property_scope.h -/// \author Folling - -#include -#include - -/// \addtogroup object_scopes ObjectScopes -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The scope of a property -struct IkarusPropertyScope; - -/// \brief Creates a property scope from a blueprint. -/// \param blueprint The blueprint the property is scoped to. -/// \return The created property scope. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusPropertyScope * ikarus_property_scope_create_blueprint(struct IkarusBlueprint * blueprint); -/// \brief Creates a property scope from an entity. -/// \param entity The entity the property is scoped to. -/// \return The created property scope. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusPropertyScope * ikarus_property_scope_create_entity(struct IkarusEntity * entity); - -/// \brief Converts a property scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -/// \remark Must be freed with #ikarus_free. -IKA_API struct IkarusObjectScope * ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope); - -/// \brief Visits a property scope, calling the appropriate function. -/// \param scope The scope to to visit -/// \param blueprint_visitor The function to call if the property is scoped to a blueprint. -/// \param entity_visitor The function to call if the property is scoped to an entity. -/// \param data The data passed to the visitor functions. -IKA_API void ikarus_property_scope_visit( - IkarusPropertyScope * scope, - void (*blueprint_visitor)(struct IkarusBlueprint *, void *), - void (*entity_visitor)(struct IkarusEntity *, void *), - void * data -); - -IKARUS_END_HEADER - -// @} diff --git a/src/folders/blueprint_folder.hpp b/src/folders/blueprint_folder.hpp deleted file mode 100644 index c98b51b..0000000 --- a/src/folders/blueprint_folder.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -struct IkarusBlueprintFolder { - IkarusId id; - std::string name_buffer; -}; diff --git a/src/folders/entity_folder.hpp b/src/folders/entity_folder.hpp deleted file mode 100644 index c69e5c7..0000000 --- a/src/folders/entity_folder.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -struct IkarusEntityFolder { - IkarusId id; - std::string name_buffer; -}; diff --git a/src/folders/folder.hpp b/src/folders/folder.hpp deleted file mode 100644 index 9589a5d..0000000 --- a/src/folders/folder.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include -#include - -struct IkarusEntityFolder { - std::variant data; -}; diff --git a/src/folders/property_folder.hpp b/src/folders/property_folder.hpp deleted file mode 100644 index 11aef0c..0000000 --- a/src/folders/property_folder.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -struct IkarusPropertyFolder { - IkarusId id; - std::string name_buffer; -}; diff --git a/src/id.cpp b/src/id.cpp index b1d28fe..cc3913f 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -1,4 +1,4 @@ -#include "ikarus/id.h" +#include "id.hpp" #include @@ -15,21 +15,11 @@ auto ikarus_id_is_equal(IkarusId left, IkarusId right) -> bool { return left == right; } -auto ikarus_id_is_none(IkarusId id) -> bool { - return ikarus_id_is_equal(id, IKARUS_ID_NONE); -} - -auto ikarus_id_is_unspecified(IkarusId id) -> bool { - return ikarus_id_is_equal(id, IKARUS_ID_UNSPECIFIED); -} - TEST_CASE("id_object_type", "[id]") { // NOLINTNEXTLINE(readability-magic-numbers) auto id = static_cast(IkarusObjectType_Blueprint) << IKARUS_ID_OBJECT_RANDOM_BITS; REQUIRE(ikarus_id_get_object_type(id) == IkarusObjectType_Blueprint); - REQUIRE(!ikarus_id_is_none(id) == IkarusObjectType_Blueprint); - REQUIRE(!ikarus_id_is_unspecified(id) == IkarusObjectType_Blueprint); } TEST_CASE("id_equal", "[id]") { @@ -40,17 +30,3 @@ TEST_CASE("id_equal", "[id]") { REQUIRE(ikarus_id_is_equal(id, copy)); REQUIRE(!ikarus_id_is_equal(id, third)); } - -TEST_CASE("id_none", "[id]") { - auto id = IKARUS_ID_NONE; - - REQUIRE(ikarus_id_is_none(id)); - REQUIRE(!ikarus_id_is_unspecified(id)); -} - -TEST_CASE("id_unspecified", "[id]") { - auto id = IKARUS_ID_UNSPECIFIED; - - REQUIRE(!ikarus_id_is_none(id)); - REQUIRE(ikarus_id_is_unspecified(id)); -} diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index f938b8f..5d5e554 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include /// \private struct IkarusBlueprint { + struct IkarusProject * project; IkarusId id; }; diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index a348533..71e3cea 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include /// \private struct IkarusEntity { + struct IkarusProject * project; IkarusId id; }; diff --git a/src/objects/object.cpp b/src/objects/object.cpp deleted file mode 100644 index fe405b9..0000000 --- a/src/objects/object.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "ikarus/objects/object.h" - -#include - -#include - -IkarusObjectType ikarus_object_get_type(IkarusObject const * object) { - return object->type; -} - -TEST_CASE("object_type", "[object]") { - auto types = { - IkarusObjectType_Blueprint, - IkarusObjectType_Property, - IkarusObjectType_Entity, - IkarusObjectType_BlueprintFolder, - IkarusObjectType_PropertyFolder, - IkarusObjectType_EntityFolder, - }; - - for (auto type : types) { - auto object = IkarusObject{.type = type}; - REQUIRE(ikarus_object_get_type(&object) == type); - } -} diff --git a/src/objects/object.hpp b/src/objects/object.hpp index 0d696aa..668cd65 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -2,14 +2,12 @@ #include -#include -#include -#include #include #include #include -struct IkarusObject { - std::variant - data; +union IkarusObject { + IkarusBlueprint blueprint; + IkarusEntity entity; + IkarusProperty property; }; diff --git a/src/objects/property.hpp b/src/objects/property.hpp index f72ad8c..5132207 100644 --- a/src/objects/property.hpp +++ b/src/objects/property.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include /// \private struct IkarusProperty { + struct IkarusProject * project; IkarusId id; }; diff --git a/src/persistence/migrations/m1_initial_layout.sql b/src/persistence/migrations/m1_initial_layout.sql new file mode 100644 index 0000000..60ecc47 --- /dev/null +++ b/src/persistence/migrations/m1_initial_layout.sql @@ -0,0 +1,111 @@ +CREATE TABLE `objects` +( + `do_not_access_rowid_alias` INTEGER PRIMARY KEY, + `object_type` INT NOT NULL, + `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56) + ) VIRTUAL, + `name` TEXT NOT NULL, + `information` TEXT NOT NULL +) STRICT; + +CREATE UNIQUE INDEX `object_id` ON `objects` (`id`); +CREATE INDEX `object_type` ON `objects` (`object_type`); + +CREATE + VIRTUAL TABLE `objects_fts` USING fts5 +( + `name`, + `information`, + content= + 'objects', + content_rowid= + 'id', + tokenize= + "unicode61 remove_diacritics 2 tokenchars '-_'" +); + +CREATE TABLE `blueprints` +( + `id` INT, + + PRIMARY KEY (`id`), + FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE +) WITHOUT ROWID, STRICT; + +CREATE TABLE `entities` +( + `id` INT, + + PRIMARY KEY (`id`), + FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE +) WITHOUT ROWID, STRICT; + +CREATE TABLE `entity_blueprints` +( + `entity` INT NOT NULL, + `blueprint` INT NOT NULL, + + PRIMARY KEY (`entity`), + UNIQUE (`entity`, `blueprint`), + FOREIGN KEY (`blueprint`) REFERENCES `blueprints` (`id`) ON DELETE CASCADE +) WITHOUT ROWID, STRICT; + +CREATE INDEX `entity_blueprints_blueprint` ON `entity_blueprints` (`blueprint`); + +CREATE TABLE `properties` +( + `id` INT, + `type` INT NOT NULL, + `default_value` TEXT NOT NULL, + `settings` TEXT NOT NULL, + + PRIMARY KEY (`id`), + FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE +) WITHOUT ROWID, STRICT; + +CREATE INDEX `properties_type` ON `properties` (`type`); + +CREATE + VIRTUAL TABLE `property_default_value_fts` USING fts5 +( + `default_value`, + content= + 'properties', + content_rowid= + 'object_id', + tokenize= + "unicode61 remove_diacritics 2 tokenchars '-_'" +); + +CREATE + VIRTUAL TABLE `property_settings_fts` USING fts5 +( + `settings`, + content= + 'properties', + content_rowid= + 'object_id', + tokenize= + "unicode61 remove_diacritics 2 tokenchars '-_'" +); + +CREATE TABLE `values` +( + `entity` INT NOT NULL, + `property` INT NOT NULL, + `value` TEXT NOT NULL, + + PRIMARY KEY (`entity`, `property`), + FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`property`) REFERENCES `properties` (`id`) ON DELETE CASCADE +) WITHOUT ROWID, STRICT; + +CREATE + VIRTUAL TABLE `values_fts` USING fts5 +( + `value`, + content= + 'values', + tokenize= + "unicode61 remove_diacritics 2 tokenchars '-_'" +) diff --git a/src/projects/project.hpp b/src/persistence/project.hpp similarity index 61% rename from src/projects/project.hpp rename to src/persistence/project.hpp index c805935..db52508 100644 --- a/src/projects/project.hpp +++ b/src/persistence/project.hpp @@ -1,10 +1,11 @@ -#include "ikarus/project/project.h" - #include #include +#include + /// \private struct IkarusProject { std::string name; std::filesystem::path path; + std::unique_ptr db; }; diff --git a/src/scopes/blueprint_scope.cpp b/src/scopes/blueprint_scope.cpp deleted file mode 100644 index c1eb0fc..0000000 --- a/src/scopes/blueprint_scope.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "ikarus/scopes/blueprint_scope.h" - -#include -#include - -IkarusBlueprintScope * ikarus_blueprint_scope_create() { - return new IkarusBlueprintScope{}; -} - -struct IkarusObjectScope * ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope) { - return new IkarusObjectScope{.data = *scope}; -} diff --git a/src/scopes/blueprint_scope.hpp b/src/scopes/blueprint_scope.hpp deleted file mode 100644 index ba0bc7d..0000000 --- a/src/scopes/blueprint_scope.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -struct IkarusBlueprintScope {}; diff --git a/src/scopes/entity_scope.cpp b/src/scopes/entity_scope.cpp deleted file mode 100644 index a0f78d0..0000000 --- a/src/scopes/entity_scope.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "ikarus/scopes/entity_scope.h" - -#include -#include - -IkarusEntityScope * ikarus_entity_scope_create() { - return new IkarusEntityScope{}; -} - -IkarusObjectScope * ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope) { - return new IkarusObjectScope{.data = *scope}; -} diff --git a/src/scopes/entity_scope.hpp b/src/scopes/entity_scope.hpp deleted file mode 100644 index 6df6550..0000000 --- a/src/scopes/entity_scope.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -struct IkarusEntityScope {}; diff --git a/src/scopes/object_scope.cpp b/src/scopes/object_scope.cpp deleted file mode 100644 index ddae9a9..0000000 --- a/src/scopes/object_scope.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "ikarus/scopes/object_scope.h" - -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include - -IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope * scope) { - return std::visit( - cppbase::overloaded{ - []([[maybe_unused]] IkarusBlueprintScope const& scope) { return IkarusObjectScopeType_Blueprint; }, - []([[maybe_unused]] IkarusPropertyScope const& scope) { return IkarusObjectScopeType_Property; }, - []([[maybe_unused]] IkarusEntityScope const& scope) { return IkarusObjectScopeType_Entity; }}, - scope->data - ); -} - -void ikarus_object_scope_visit( - IkarusObjectScope * scope, - void(blueprint_visitor)(IkarusBlueprintScope *, void *), - void(property_visitor)(IkarusPropertyScope *, void *), - void(entity_visitor)(IkarusEntityScope *, void *), - void * data -) { - std::visit( - cppbase::overloaded{ - [blueprint_visitor, data](IkarusBlueprintScope& scope) { blueprint_visitor(&scope, data); }, - [property_visitor, data](IkarusPropertyScope& scope) { property_visitor(&scope, data); }, - [entity_visitor, data](IkarusEntityScope& scope) { entity_visitor(&scope, data); }}, - scope->data - ); -} - -TEST_CASE("blueprint_object_scope_conversion", "[object_scope]") { - auto * blueprint_scope = ikarus_blueprint_scope_create(); - auto * blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); - REQUIRE(ikarus_object_scope_get_type(blueprint_object_scope) == IkarusObjectScopeType_Blueprint); -} - -TEST_CASE("property_object_scope_conversion", "[object_scope]") { - auto * blueprint = ikarus_blueprint_create(); - auto * entity = ikarus_entity_create(); - - auto * property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); - auto * property_blueprint_object_scope = ikarus_property_scope_to_object_scope(property_blueprint_scope); - - REQUIRE(ikarus_object_scope_get_type(property_blueprint_object_scope) == IkarusObjectScopeType_Property); - - auto * property_entity_scope = ikarus_property_scope_create_entity(entity); - auto * property_entity_object_scope = ikarus_property_scope_to_object_scope(property_entity_scope); - - REQUIRE(ikarus_object_scope_get_type(property_entity_object_scope) == IkarusObjectScopeType_Property); -} - -TEST_CASE("entity_object_scope_conversion", "[object_scope]") { - auto * entity_scope = ikarus_entity_scope_create(); - auto * entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); - REQUIRE(ikarus_object_scope_get_type(entity_object_scope) == IkarusObjectScopeType_Entity); -} - -TEST_CASE("object_scope_visiting", "[object_scope]") { - auto * blueprint = ikarus_blueprint_create(); - - auto * blueprint_scope = ikarus_blueprint_scope_create(); - auto * property_scope = ikarus_property_scope_create_blueprint(blueprint); - auto * entity_scope = ikarus_entity_scope_create(); - - auto * blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); - auto * property_object_scope = ikarus_property_scope_to_object_scope(property_scope); - auto * entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); - - auto scopes = { - std::make_pair(blueprint_object_scope, 1), - std::make_pair(property_object_scope, 2), - std::make_pair(entity_object_scope, 3), - }; - - for (auto [scope, value] : scopes) { - int test = 0; - - ikarus_object_scope_visit( - scope, - [](IkarusBlueprintScope *, void * data) { *static_cast(data) = 1; }, - [](IkarusPropertyScope *, void * data) { *static_cast(data) = 2; }, - [](IkarusEntityScope *, void * data) { *static_cast(data) = 3; }, - &test - ); - - REQUIRE(test == value); - } -} diff --git a/src/scopes/object_scope.hpp b/src/scopes/object_scope.hpp deleted file mode 100644 index 9bc3b09..0000000 --- a/src/scopes/object_scope.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -struct IkarusObjectScope { - std::variant data; -}; diff --git a/src/scopes/property_scope.cpp b/src/scopes/property_scope.cpp deleted file mode 100644 index 26f45f5..0000000 --- a/src/scopes/property_scope.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "ikarus/scopes/property_scope.h" - -#include - -#include -#include -#include -#include - -IkarusPropertyScope * ikarus_property_scope_create_blueprint(IkarusBlueprint * blueprint) { - return new IkarusPropertyScope{.data = *blueprint}; -} - -IkarusPropertyScope * ikarus_property_scope_create_entity(IkarusEntity * entity) { - return new IkarusPropertyScope{.data = *entity}; -} - -IkarusObjectScope * ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope) { - return new IkarusObjectScope{.data = *scope}; -} - -void ikarus_property_scope_visit( - IkarusPropertyScope * scope, - void (*blueprint_func)(IkarusBlueprint *, void *), - void (*entity_func)(IkarusEntity *, void *), - void * data -) { - std::visit( - cppbase::overloaded{ - [blueprint_func, data](IkarusBlueprint& blueprint) { blueprint_func(&blueprint, data); }, - [entity_func, data](IkarusEntity& entity) { entity_func(&entity, data); }, - }, - scope->data - ); -} diff --git a/src/scopes/property_scope.hpp b/src/scopes/property_scope.hpp deleted file mode 100644 index 4d3aad0..0000000 --- a/src/scopes/property_scope.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include - -struct IkarusPropertyScope { - std::variant data; -}; diff --git a/src/values/value.cpp b/src/values/value.cpp deleted file mode 100644 index a5d6457..0000000 --- a/src/values/value.cpp +++ /dev/null @@ -1,343 +0,0 @@ -#include "ikarus/values/value.h" - -#include -#include -#include -#include -#include - -#include - -#include -#include - -/// \brief Creates an indeterminate entity value of a given type. -/// \param type The type of the value. -/// \return The entity value. -IKA_API IkarusEntityValue value_create_invalid(IkarusPropertyType type) { - return IkarusEntityValue{ - ._type = type, - ._data = IkarusEntityValueData{}, - ._state = IkarusEntityValueState_Invalid, - }; -} - -IkarusEntityValue ikarus_value_create_toggle(bool value) { - return IkarusEntityValue{ - ._type = IkarusPropertyType_Toggle, - ._data = IkarusEntityValueData{._toggle = IkarusToggleValue{._value = value}}, - ._state = IkarusEntityValueState_Normal, - }; -} - -IkarusEntityValue ikarus_value_create_number(long double value) { - if (auto fp_class = std::fpclassify(value); fp_class != FP_NORMAL && fp_class != FP_ZERO) { - return value_create_invalid(IkarusPropertyType_Number); - } - - return IkarusEntityValue{ - ._type = IkarusPropertyType_Number, - ._data = IkarusEntityValueData{._number = IkarusNumberValue{._value = value}}, - ._state = IkarusEntityValueState_Normal, - }; -} - -IkarusEntityValue ikarus_value_create_text(char const * value) { - if (value == nullptr) { - return value_create_invalid(IkarusPropertyType_Text); - } - - return IkarusEntityValue{ - ._type = IkarusPropertyType_Text, - ._data = IkarusEntityValueData{._text = IkarusTextValue{._value = value}}, - ._state = IkarusEntityValueState_Normal, - }; -} - -IkarusEntityValue ikarus_value_create_indeterminate(IkarusPropertyType type) { - IkarusEntityValueData data{}; - - switch (type) { - case IkarusPropertyType_Toggle: { - data._toggle = IkarusToggleValue{._value = false}; - break; - } - case IkarusPropertyType_Number: { - data._number = IkarusNumberValue{._value = 0.0}; - break; - } - case IkarusPropertyType_Text: { - data._text = IkarusTextValue{._value = ""}; - break; - } - default: return value_create_invalid(type); - } - - return IkarusEntityValue{ - ._type = type, - ._data = data, - ._state = IkarusEntityValueState_Indeterminate, - }; -} - -IkarusEntityValue ikarus_value_get_default(IkarusPropertyType type) { - switch (type) { - case IkarusPropertyType_Toggle: return ikarus_value_create_toggle(false); - case IkarusPropertyType_Number: return ikarus_value_create_number(0.0); - case IkarusPropertyType_Text: return ikarus_value_create_text(""); - default: return value_create_invalid(type); - } -} - -bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value) { - return value->_value; -} - -long double ikarus_number_value_get_underlying(IkarusNumberValue const * value) { - return value->_value; -} - -char const * ikarus_text_value_get_underlying(IkarusTextValue const * value) { - return value->_value; -} - -// no need to check for validity here, since these concrete types are only created by the library -bool ikarus_toggle_value_is_equal(IkarusToggleValue const * value, bool check) { - return value != nullptr && value->_value == check; -} - -bool ikarus_number_value_is_equal(IkarusNumberValue const * value, long double check) { - return value != nullptr && value->_value == check; -} - -bool ikarus_text_value_is_equal(IkarusTextValue const * value, char const * check) { - return value != nullptr && check != nullptr && std::strcmp(value->_value, check) == 0; -} - -bool ikarus_value_is_equal(IkarusEntityValue const * left, IkarusEntityValue const * right) { - if (left == nullptr || right == nullptr) { - return false; - } - - if (left->_state == IkarusEntityValueState_Invalid || right->_state == IkarusEntityValueState_Invalid) { - return false; - } - - if (left->_type != right->_type) { - return false; - } - - // indeterminate values are only equal if they have the same type - if (left->_state == IkarusEntityValueState_Indeterminate && right->_state == IkarusEntityValueState_Indeterminate) { - return true; - } - - switch (left->_type) { - case IkarusPropertyType_Toggle: return left->_data._toggle._value == right->_data._toggle._value; - case IkarusPropertyType_Number: return left->_data._number._value == right->_data._number._value; - case IkarusPropertyType_Text: return std::strcmp(left->_data._text._value, right->_data._text._value) == 0; - default: return false; - } -} - -bool ikarus_value_is_invalid(IkarusEntityValue const * value) { - return value == nullptr || value->_state == IkarusEntityValueState_Invalid; -} - -IkarusPropertyType ikarus_value_get_type(IkarusEntityValue const * value) { - return value->_type; -} - -void ikarus_value_visit( - IkarusEntityValue const * value, - void (*toggle)(IkarusToggleValue const * value, void * data), - void (*number)(IkarusNumberValue const * value, void * data), - void (*text)(IkarusTextValue const * value, void * data), - void * data -) { - if (value == nullptr) { - return; - } - - switch (value->_type) { - case IkarusPropertyType_Toggle: { - if (toggle != nullptr) { - toggle(&value->_data._toggle, data); - } - break; - } - case IkarusPropertyType_Number: { - if (number != nullptr) { - number(&value->_data._number, data); - } - break; - } - case IkarusPropertyType_Text: { - if (text != nullptr) { - text(&value->_data._text, data); - } - break; - } - default: break; - } -} - -TEST_CASE("toggle_value_creation", "[value]") { - auto toggle_value = ikarus_value_create_toggle(true); - - REQUIRE(ikarus_value_get_type(&toggle_value) == IkarusPropertyType_Toggle); - REQUIRE(ikarus_toggle_value_is_equal(&toggle_value._data._toggle, true)); -} - -TEST_CASE("number_value_creation", "[value]") { - auto number_value = ikarus_value_create_number(1.0); - - REQUIRE(ikarus_value_get_type(&number_value) == IkarusPropertyType_Number); - REQUIRE(ikarus_number_value_is_equal(&number_value._data._number, 1.0)); - - auto nan_value = ikarus_value_create_number(std::numeric_limits::quiet_NaN()); - REQUIRE(ikarus_value_is_invalid(&nan_value)); - auto signaling_non_value = ikarus_value_create_number(std::numeric_limits::signaling_NaN()); - REQUIRE(ikarus_value_is_invalid(&signaling_non_value)); - auto inf_value = ikarus_value_create_number(std::numeric_limits::infinity()); - REQUIRE(ikarus_value_is_invalid(&inf_value)); - auto neg_inf_value = ikarus_value_create_number(-std::numeric_limits::infinity()); - REQUIRE(ikarus_value_is_invalid(&neg_inf_value)); -} - -TEST_CASE("text_value_creation", "[value]") { - auto text_value = ikarus_value_create_text("test"); - - REQUIRE(ikarus_value_get_type(&text_value) == IkarusPropertyType_Text); - REQUIRE(ikarus_text_value_is_equal(&text_value._data._text, "test")); - - auto null_value = ikarus_value_create_text(nullptr); - REQUIRE(ikarus_value_is_invalid(&null_value)); -} - -TEST_CASE("default_value_creation", "[value]") { - auto types = { - IkarusPropertyType_Toggle, - IkarusPropertyType_Number, - IkarusPropertyType_Text, - }; - - for (auto type : types) { - auto value = ikarus_value_get_default(type); - REQUIRE(ikarus_value_get_type(&value) == type); - } -} - -TEST_CASE("toggle_value_underlying", "[value]") { - auto true_toggle_value = ikarus_value_create_toggle(true); - auto false_toggle_value = ikarus_value_create_toggle(false); - - REQUIRE(ikarus_toggle_value_get_underlying(&true_toggle_value._data._toggle) == true); - REQUIRE(ikarus_toggle_value_get_underlying(&false_toggle_value._data._toggle) == false); -} - -TEST_CASE("number_value_underlying", "[value]") { - auto zero_number_value = ikarus_value_create_number(0.0); - auto third_number_value = ikarus_value_create_number(1.0 / 3.0); - auto large_number_value = ikarus_value_create_number(1.2345678910e123); - - REQUIRE(ikarus_number_value_get_underlying(&zero_number_value._data._number) == 0.0); - REQUIRE(ikarus_number_value_get_underlying(&third_number_value._data._number) == 1.0 / 3.0); - REQUIRE(ikarus_number_value_get_underlying(&large_number_value._data._number) == 1.2345678910e123); -} - -TEST_CASE("text_value_underlying", "[value]") { - auto test_value = ikarus_value_create_text("test"); - auto empty_value = ikarus_value_create_text(""); - - REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&test_value._data._text), "test") == 0); - REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&empty_value._data._text), "") == 0); -} - -TEST_CASE("toggle_comparison", "[value]") { - auto true_toggle_value = ikarus_value_create_toggle(true); - auto false_toggle_value = ikarus_value_create_toggle(false); - - REQUIRE(ikarus_toggle_value_is_equal(&true_toggle_value._data._toggle, true)); - REQUIRE(ikarus_toggle_value_is_equal(&false_toggle_value._data._toggle, false)); -} - -TEST_CASE("number_comparison", "[value]") { - auto zero_number_value = ikarus_value_create_number(0.0); - auto third_number_value = ikarus_value_create_number(1.0 / 3.0); - auto large_number_value = ikarus_value_create_number(1.2345678910e123); - - REQUIRE(ikarus_number_value_is_equal(&zero_number_value._data._number, 0.0)); - REQUIRE(ikarus_number_value_is_equal(&third_number_value._data._number, 1.0 / 6.0 + 1.0 / 6.0)); - REQUIRE(ikarus_number_value_is_equal(&large_number_value._data._number, 1.2345678910e123)); -} - -TEST_CASE("text_comparison", "[value]") { - auto test_value = ikarus_value_create_text("test"); - auto empty_value = ikarus_value_create_text(""); - - REQUIRE(ikarus_text_value_is_equal(&test_value._data._text, "test")); - REQUIRE(ikarus_text_value_is_equal(&empty_value._data._text, "")); -} - -TEST_CASE("value_comparison", "[value]") { - auto true_toggle_value = ikarus_value_create_toggle(true); - auto false_toggle_value = ikarus_value_create_toggle(false); - auto number_value1 = ikarus_value_create_number(0.0); - auto number_value2 = ikarus_value_create_number(0.0); - auto invalid_value = ikarus_value_create_text(nullptr); - - auto indeterminate_toggle = ikarus_value_create_indeterminate(IkarusPropertyType_Toggle); - auto indeterminate_number1 = ikarus_value_create_indeterminate(IkarusPropertyType_Number); - auto indeterminate_number2 = ikarus_value_create_indeterminate(IkarusPropertyType_Number); - - REQUIRE(!ikarus_value_is_equal(nullptr, nullptr)); - REQUIRE(!ikarus_value_is_equal(&true_toggle_value, nullptr)); - REQUIRE(!ikarus_value_is_equal(nullptr, &true_toggle_value)); - - REQUIRE(!ikarus_value_is_equal(&invalid_value, &invalid_value)); - REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &invalid_value)); - REQUIRE(!ikarus_value_is_equal(&invalid_value, &true_toggle_value)); - - REQUIRE(ikarus_value_is_equal(&true_toggle_value, &true_toggle_value)); - REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &false_toggle_value)); - REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &number_value1)); - REQUIRE(!ikarus_value_is_equal(&number_value1, &true_toggle_value)); - REQUIRE(ikarus_value_is_equal(&number_value1, &number_value2)); - - REQUIRE(!ikarus_value_is_equal(&indeterminate_toggle, &indeterminate_number1)); - REQUIRE(ikarus_value_is_equal(&indeterminate_number1, &indeterminate_number2)); -} - -TEST_CASE("invalid_value", "[value]") { - auto invalid_value = ikarus_value_create_toggle(false); - invalid_value._state = IkarusEntityValueState_Invalid; - - REQUIRE(ikarus_value_is_invalid(&invalid_value)); -} - -TEST_CASE("visit_value", "[value]") { - auto toggle_value = ikarus_value_create_toggle(true); - auto number_value = ikarus_value_create_number(0.0); - auto text_value = ikarus_value_create_text("test"); - - auto values = { - std::make_pair(toggle_value, 1), - std::make_pair(number_value, 2), - std::make_pair(text_value, 3), - }; - - for (auto [value, expected] : values) { - int test = 0; - - ikarus_value_visit( - &value, - [](IkarusToggleValue const *, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusNumberValue const *, void * data) { *reinterpret_cast(data) = 2; }, - [](IkarusTextValue const *, void * data) { *reinterpret_cast(data) = 3; }, - &test - ); - - REQUIRE(test == expected); - } -} From 43e4f85cf2e283e1ef06b031f6aaebef35af553a Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 14 Nov 2023 16:29:41 +0100 Subject: [PATCH 009/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index a397a5e..3057656 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit a397a5e8c35c7b30ecac8b4994301a585e24560b +Subproject commit 3057656ff277294ab424af90e553e630c2a5e8f7 From 8be368f461bc1187021a09b016da7ba494ee3d5d Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 18 Nov 2023 02:10:20 +0100 Subject: [PATCH 010/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 3057656..806c264 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 3057656ff277294ab424af90e553e630c2a5e8f7 +Subproject commit 806c26457c4e9e3d613e63a8511150390c1b196d From bdd4b4db951c49c3815dab23ea93d73e53ddcaa1 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 18 Nov 2023 02:10:38 +0100 Subject: [PATCH 011/166] implement blueprint functionality & streamline API Signed-off-by: Folling --- include/ikarus/objects/blueprint.h | 67 +-------- include/ikarus/objects/entity.h | 100 +------------ include/ikarus/objects/property.h | 55 +------ src/objects/blueprint.cpp | 135 ++++++++++++++++++ src/objects/blueprint.hpp | 8 +- src/objects/entity.hpp | 7 +- src/objects/object.hpp | 15 +- src/objects/property.hpp | 8 +- .../migrations/m1_initial_layout.sql | 9 +- 9 files changed, 175 insertions(+), 229 deletions(-) create mode 100644 src/objects/blueprint.cpp diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index ed35c89..85f40eb 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -28,55 +28,13 @@ struct IkarusBlueprint; /// \remark Must be freed using #ikarus_free. IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name); -/// \brief Creates a blueprint from an entity. -/// \details The created blueprint will have the same properties as the entity. -/// \param entity The entity to create the blueprint from. -/// \pre \li Must not be null. -/// \param link_entity If true, the entity will be linked to the blueprint. If not they will remain separate. -/// \param name The name of the blueprint. Must not be empty. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created blueprint or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprint * ikarus_blueprint_create_from_entity( - struct IkarusEntity * entity, bool link_entity, char const * name -); - -/// \brief Copies a blueprint. -/// \details Creates a deep copy of the blueprint including all of its properties. -/// \param blueprint The blueprint to copy. -/// \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. -/// \return The created blueprint or null if an error occurs. -/// \remark Linked entities won't be copied. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprint * ikarus_blueprint_copy(IkarusBlueprint const * blueprint, char const * name); - -/// \brief Deletes a blueprint. +/// \brief Deletes & frees a blueprint. /// \param blueprint The blueprint to delete. /// \pre \li Must not be null. /// \pre \li Must exist. /// \remark The blueprint must not be accessed after deletion. IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); -/// \brief Gets the project of a blueprint. -/// \param blueprint The blueprint to get the project of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The project of the blueprint or null if an error occurs. -IKA_API struct IkarusProject * ikarus_blueprint_get_project(IkarusBlueprint const * blueprint); - -/// \brief Gets the name of a blueprint. -/// \param blueprint The blueprint to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the blueprint or null if an error occurs. -/// \remark The returned pointer is valid until the blueprint is freed but may be invalidated by other operations. -IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint); - /// \brief Gets the number of properties of a blueprint. /// \param blueprint The blueprint to get the number of properties of. /// \pre \li Must not be null. @@ -113,26 +71,13 @@ IKA_API void ikarus_blueprint_get_linked_entities( IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size ); -/// \brief Sets the name of a blueprint. -/// \param blueprint The blueprint to set the name of. +/// \brief Casts a blueprint to an object. +/// \param blueprint The blueprint to cast. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param new_name The new name of the blueprint. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_blueprint_set_name(IkarusBlueprint * blueprint, char const * new_name); - -/// \brief Compares two blueprints. -/// \param left The left blueprint to compare. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param right The right blueprint to compare. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return True if the two blueprints are equal, false otherwise. -/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two -/// blueprints reference the same blueprint in the same project. -IKA_API bool ikarus_blueprint_is_equal(IkarusBlueprint const * left, IkarusBlueprint const * right); +/// \return The blueprint represented as an object or null if an error occurs. +/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. +IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint const * blueprint); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index e73e18d..813adea 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -58,25 +58,6 @@ IKA_API IkarusEntity * ikarus_entity_create( size_t blueprints_count ); -/// \brief Copies an entity. -/// \details Creates a deep copy of the entity including all of its properties & associated values. -/// \param entity The entity to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the entity. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the entity in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the entity. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created entity or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusEntity * ikarus_entity_copy( - struct IkarusEntity * entity, struct IkarusEntityFolder * parent, size_t position, char const * name -); - /// \brief Deletes an entity. /// \param entity The entity to delete. /// \pre \li Must not be null. @@ -117,43 +98,6 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru /// \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); -/// \brief Gets the project of an entity. -/// \param entity The entity to get the project of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The project of the entity or null if an error occurs. -IKA_API struct IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity); - -/// \brief Gets the parent folder of an entity. -/// \param entity The entity to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the entity or null if an error occurs. -IKA_API struct IkarusEntityFolder * ikarus_entity_get_parent(IkarusEntity const * entity); - -/// \brief Gets the position of an entity within its parent folder. -/// \param entity The entity to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the entity or undefined if an error occurs. -IKA_API size_t ikarus_entity_get_position(IkarusEntity const * entity); - -/// \brief Gets the name of an entity. -/// \param entity The entity to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the entity or null if an error occurs. -/// \remark The returned pointer is valid until the entity is freed but may be invalidated by other operations. -IKA_API char const * ikarus_entity_get_name(IkarusEntity const * entity); - -/// \brief Gets the property root folder of an entity. -/// \param entity The entity to get the root folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The root folder of all properties of the entity or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyFolder * ikarus_entity_get_property_root_folder(IkarusEntity const * entity); - /// \brief Gets the number of properties of an entity. /// \param entity The entity to get the number of properties of. /// \pre \li Must not be null. @@ -184,36 +128,6 @@ IKA_API void ikarus_entity_get_properties( /// \remark Must be freed using #ikarus_free. IKA_API struct IkarusEntityValue * get_value(IkarusEntity const * entity, struct IkarusProperty const * property); -/// \brief Sets the parent folder of an entity. -/// \param entity The entity to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the entity. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the entity in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_entity_set_parent(IkarusEntity * entity, struct IkarusEntityFolder * new_parent, size_t new_position); - -/// \brief Sets the position of an entity within its parent folder. -/// \param entity The entity to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the entity. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_entity_set_position(IkarusEntity * entity, size_t new_position); - -/// \brief Sets the name of an entity. -/// \param entity The entity to set the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_name The new name of the entity. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_entity_set_name(IkarusEntity * entity, char const * new_name); - /// \brief Sets the value of a property of an entity. /// \param entity The entity to set the value of. /// \pre \li Must not be null. @@ -231,17 +145,13 @@ IKA_API void ikarus_entity_set_value( IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue * value, bool validate_settings ); -/// \brief Compares two entities. -/// \param left The left entity to compare. +/// \brief Casts an entity to an object. +/// \param entity The entity to cast. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param right The right entity to compare. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return True if the two entities are equal, false otherwise. -/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two -/// entities reference the same entity in the same project. -IKA_API bool ikarus_entity_is_equal(IkarusEntity const * left, IkarusEntity const * right); +/// \return The entity represented as an object or null if an error occurs. +/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. +IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity const * entity); IKARUS_END_HEADER diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index 10258df..a62a821 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -73,23 +73,6 @@ IKA_API struct IkarusProperty * ikarus_property_create( struct IkarusPropertyTypeInfo * property_info ); -/// \brief Copies a property. -/// \details Creates a deep copy of the property including all of its settings and associated values. -/// \param property The property to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param source The source to copy the property to. -/// \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. -/// \return The created property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusProperty * ikarus_property_copy( - struct IkarusProperty * property, struct IkarusPropertySource * source, char const * name -); - /// \brief Deletes a property. /// \param property The property to delete. /// \pre \li Must not be null. @@ -97,21 +80,6 @@ IKA_API struct IkarusProperty * ikarus_property_copy( /// \remark The property must not be accessed after deletion. IKA_API void ikarus_property_delete(struct IkarusProperty * property); -/// \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. -/// \return The project of the property or null if an error occurs. -IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty const * property); - -/// \brief Gets the name of a property. -/// \param property The property to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the property or null if an error occurs. -/// \remark The returned pointer is valid until the property is freed but may be invalidated by other operations. -IKA_API char const * ikarus_property_get_name(IkarusProperty const * property); - /// \brief Gets the type info of a property. /// \param property The property to get the type info of. /// \pre \li Must not be null. @@ -136,15 +104,6 @@ IKA_API struct IkarusPropertySource * ikarus_property_get_source(IkarusProperty /// \remark Must be freed using #ikarus_free. IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); -/// \brief Sets 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 new_name The new name of the property. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_property_set_name(IkarusProperty * property, char const * new_name); - /// \brief Sets the type info of a property and resets all values to the new default value. /// \param property The property to set the type info of. /// \pre \li Must not be null. @@ -156,17 +115,13 @@ IKA_API void ikarus_property_set_type_info( IkarusProperty * property, struct IkarusPropertyTypeInfo new_type_info, bool attempt_conversion ); -/// \brief Compares two properties. -/// \param left The left property to compare. +/// \brief Casts a property to an object. +/// \param property The property to cast. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param right The right property to compare. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return True if the two properties are equal, false otherwise. -/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two -/// properties reference the same property in the same project. -IKA_API bool ikarus_property_is_equal(IkarusProperty const * left, IkarusProperty const * right); +/// \return The property represented as an object or null if an error occurs. +/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. +IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty const * property); IKARUS_END_HEADER diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp new file mode 100644 index 0000000..3d1c997 --- /dev/null +++ b/src/objects/blueprint.cpp @@ -0,0 +1,135 @@ +#include "blueprint.hpp" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { + LOG_INFO("creating new blueprint"); + + if (project == nullptr) { + LOG_ERROR("project is nullptr"); + return nullptr; + } + + LOG_VERBOSE("project={}; name={}", project->path.c_str(), name); + + if (name == nullptr) { + LOG_ERROR("name is nullptr"); + return nullptr; + } + + if (cppbase::is_empty_or_blank(name)) { + LOG_ERROR("name is empty or blank"); + return nullptr; + } + + VTRYRV(auto id, nullptr, project->db->transact([name](auto * db) -> cppbase::Result { + LOG_VERBOSE("creating blueprint in objects table"); + + TRY(db->execute( + "INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(IkarusObjectType_Blueprint), name + )); + + auto id = db->last_insert_rowid(); + + LOG_VERBOSE("blueprint is {}", id); + + LOG_VERBOSE("inserting blueprint into blueprints table"); + + TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?);", id)); + + return cppbase::ok(id); + })); + + LOG_VERBOSE("successfully created blueprint"); + + return new IkarusBlueprint{project, id}; +} + +void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { + LOG_INFO("deleting blueprint"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return; + } + + LOG_VERBOSE("blueprint={}", blueprint->id); + + if (auto res = blueprint->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id); res.is_err()) { + LOG_ERROR("failed to delete blueprint {} from objects table: {}", blueprint->id, res.unwrap_error()); + return; + } + + LOG_VERBOSE("blueprint was successfully deleted from database, freeing pointer"); + + delete blueprint; + + LOG_VERBOSE("successfully deleted blueprint"); +} + +size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { + LOG_VERBOSE("fetching blueprint property count"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return 0; + } + + LOG_VERBOSE("blueprint={}", blueprint->id); + + VTRYRV( + auto count, + 0, + blueprint->project->db->query_one( + "SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint_id` = ?;", blueprint->id + ) + ); + + return static_cast(count); +} + +void ikarus_blueprint_get_properties( + IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size +) { + LOG_VERBOSE("fetching blueprint properties"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return; + } + + if (properties_out == nullptr) { + LOG_ERROR("properties_out is nullptr"); + return; + } + + LOG_VERBOSE("blueprint={}; properties_out_size={}", blueprint->id, properties_out_size); + + IkarusId ids[properties_out_size]; + + if (auto res = blueprint->project->db->query_many_buffered( + "SELECT `id` FROM `properties` WHERE `source` = ?", static_cast(ids), properties_out_size, blueprint->id + ); + res.is_err()) { + LOG_ERROR("failed to fetch blueprint property ids: {}", res.unwrap_error()); + return; + } + + for (size_t i = 0; i < properties_out_size; ++i) { + /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + properties_out[i] = new IkarusProperty{blueprint->project, ids[i]}; + } + + LOG_VERBOSE("successfully fetched blueprint properties"); +} diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index 5d5e554..f904074 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -1,9 +1,9 @@ #pragma once -#include +#include /// \private -struct IkarusBlueprint { - struct IkarusProject * project; - IkarusId id; +struct IkarusBlueprint : public IkarusObject { + inline IkarusBlueprint(struct IkarusProject * project, IkarusId id): + IkarusObject{project, id} {} }; diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index 71e3cea..91b3d3e 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -1,9 +1,6 @@ #pragma once -#include +#include /// \private -struct IkarusEntity { - struct IkarusProject * project; - IkarusId id; -}; +struct IkarusEntity : public IkarusObject {}; diff --git a/src/objects/object.hpp b/src/objects/object.hpp index 668cd65..e95c3e0 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -2,12 +2,13 @@ #include -#include -#include -#include +#include -union IkarusObject { - IkarusBlueprint blueprint; - IkarusEntity entity; - IkarusProperty property; +struct IkarusObject { + struct IkarusProject * project; + IkarusId id; + + inline IkarusObject(struct IkarusProject * project, IkarusId id): + project{project}, + id{id} {} }; diff --git a/src/objects/property.hpp b/src/objects/property.hpp index 5132207..b59d7dd 100644 --- a/src/objects/property.hpp +++ b/src/objects/property.hpp @@ -1,9 +1,9 @@ #pragma once -#include +#include /// \private -struct IkarusProperty { - struct IkarusProject * project; - IkarusId id; +struct IkarusProperty : public IkarusObject { + inline IkarusProperty(struct IkarusProject * project, IkarusId id): + IkarusObject{project, id} {} }; diff --git a/src/persistence/migrations/m1_initial_layout.sql b/src/persistence/migrations/m1_initial_layout.sql index 60ecc47..7b49cb6 100644 --- a/src/persistence/migrations/m1_initial_layout.sql +++ b/src/persistence/migrations/m1_initial_layout.sql @@ -2,8 +2,7 @@ CREATE TABLE `objects` ( `do_not_access_rowid_alias` INTEGER PRIMARY KEY, `object_type` INT NOT NULL, - `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56) - ) VIRTUAL, + `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56)) VIRTUAL, `name` TEXT NOT NULL, `information` TEXT NOT NULL ) STRICT; @@ -47,6 +46,7 @@ CREATE TABLE `entity_blueprints` PRIMARY KEY (`entity`), UNIQUE (`entity`, `blueprint`), + FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, FOREIGN KEY (`blueprint`) REFERENCES `blueprints` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; @@ -58,12 +58,15 @@ CREATE TABLE `properties` `type` INT NOT NULL, `default_value` TEXT NOT NULL, `settings` TEXT NOT NULL, + `source` INT NOT NULL, PRIMARY KEY (`id`), - FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE + FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`source`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; CREATE INDEX `properties_type` ON `properties` (`type`); +CREATE INDEX `properties_source` ON `properties` (`source`); CREATE VIRTUAL TABLE `property_default_value_fts` USING fts5 From 424bd22aa5208187d906f9a669cbd85d1e11a643 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 20 Nov 2023 08:58:58 +0100 Subject: [PATCH 012/166] cache entities to avoid allocations Signed-off-by: Folling --- include/ikarus/errors.h | 71 ++++++++++++++++++ include/ikarus/global.h | 6 +- src/errors.cpp | 22 ++++++ src/objects/blueprint.cpp | 114 ++++++++++++++++++++-------- src/objects/entity.hpp | 5 +- src/persistence/project.hpp | 143 +++++++++++++++++++++++++++++++++++- 6 files changed, 324 insertions(+), 37 deletions(-) create mode 100644 include/ikarus/errors.h create mode 100644 src/errors.cpp diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h new file mode 100644 index 0000000..f501c22 --- /dev/null +++ b/include/ikarus/errors.h @@ -0,0 +1,71 @@ +#pragma once + +/// \file global.h +/// \author Folling + +#include + +/// \addtogroup errors Errors +/// \brief Error handling within libikarus +/// \details Errors are stored for each project, akin to the errno handling in C. +/// We store multiple pieces of information about the error occurring. For more information see +/// #ikarus_project_get_error_message. +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief Delineates what caused an error. +/// \details First 2 bytes delineate the major type, next 2 bytes delineate the minor type, next 4 bytes delineate the detail +/// type. +/// \remark Note that this doesn't show responsibility. An error with source "SubSystem" could still be the fault of +/// libikarus. +enum IkarusErrorInfo { + /// \brief No error occurred. + IkarusErrorInfo_Source_None = 0x0001000000000000, + /// \brief The error was caused by the client. + IkarusErrorInfo_Source_Client = 0x0001000000000001, + /// \brief The error was caused by a sub-system of libikarus. + IkarusErrorInfo_Source_SubSystem = 0x0001000000000002, + /// \brief The error was caused by libikarus itself. + IkarusErrorInfo_Source_LibIkarus = 0x0001000000000003, + /// \brief The error was caused by an unknown source. + IkarusErrorInfo_Source_Unknown = 0x00010000FFFFFFFF, + /// \brief No error occurred. + IkarusErrorInfo_Type_None = 0x0002000000000000, + /// \brief The user misused the API. + /// \example Accessing a resource that does not exist. + IkarusErrorInfo_Type_Client_Misuse = 0x0002000100000001, + /// \brief The user provided invalid input. + /// \example Passing null for a pointer that must not be null. + IkarusErrorInfo_Type_Client_Input = 0x0002000100000002, + /// \brief An error occurred while interacting with a dependency from ikarus. + /// \example An error occurred in the underlying OS library. + IkarusErrorInfo_Type_SubSystem_Dependency = 0x0002000200000001, + /// \brief An error occurred while interacting with the database. + /// \example An error occurred while executing a query. + IkarusErrorInfo_Type_SubSystem_Database = 0x0002000200000002, + /// \brief An error occurred while interacting with the filesystem. + /// \example An error occurred while reading a file. + IkarusErrorInfo_Type_SubSystem_Filesystem = 0x0002000200000003, + /// \brief A datapoint within ikarus is invalid for the current state of the system. + /// \example The name of an object is found to be invalid UTF8. + IkarusErrorInfo_Type_LibIkarus_InvalidState = 0x0002000300000001, + /// \brief LibIkarus is unable to perform a certain operation that should succeed. + /// \example Migrating a project fails + IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation = 0x0002000300000002, + /// \brief LibIkarus is unable to perform a certain operation within a given timeframe. + /// \example A query takes longer than the timeout. + IkarusErrorInfo_Type_LibIkarus_Timeout = 0x0002000300000003, + /// \brief The type of error is unknown. + IkarusErrorInfo_Type_Unknown = 0xFFFFFFFF, +}; + +/// \brief Gets the name of an error info. +/// \param info The error info to get the name of. +/// \return The name of the error info. +/// \remark The returned pointer is valid for the lifetime of the program and must not be freed. +IKA_API char const * get_error_info_name(IkarusErrorInfo info); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/global.h b/include/ikarus/global.h index 6d91227..1572b24 100644 --- a/include/ikarus/global.h +++ b/include/ikarus/global.h @@ -1,6 +1,6 @@ #pragma once -/// \file memory.h +/// \file global.h /// \author Folling #include @@ -9,8 +9,12 @@ /// \brief Information relevant to the entire library. /// @{ +IKARUS_BEGIN_HEADER + /// \brief Frees a pointer allocated by ikarus. Every pointer returned by a function must be freed using this function unless /// explicitly stated otherwise. IKA_API void ikarus_free(void * ptr); +IKARUS_END_HEADER + /// @} diff --git a/src/errors.cpp b/src/errors.cpp new file mode 100644 index 0000000..19c3bf9 --- /dev/null +++ b/src/errors.cpp @@ -0,0 +1,22 @@ +#include "ikarus/errors.h" + +char const * get_error_info_name(IkarusErrorInfo info) { + switch (info) { + case IkarusErrorInfo_Source_None: return "IkarusErrorSource_None"; + case IkarusErrorInfo_Source_Client: return "IkarusErrorSource_Client"; + case IkarusErrorInfo_Source_SubSystem: return "IkarusErrorSource_SubSystem"; + case IkarusErrorInfo_Source_LibIkarus: return "IkarusErrorSource_LibIkarus"; + case IkarusErrorInfo_Source_Unknown: return "IkarusErrorSource_Unknown"; + case IkarusErrorInfo_Type_None: return "IkarusErrorType_None"; + case IkarusErrorInfo_Type_Client_Misuse: return "IkarusErrorType_Client_Misuse"; + case IkarusErrorInfo_Type_Client_Input: return "IkarusErrorType_Client_Input"; + case IkarusErrorInfo_Type_SubSystem_Dependency: return "IkarusErrorType_SubSystem_Dependency"; + case IkarusErrorInfo_Type_SubSystem_Database: return "IkarusErrorType_SubSystem_Database"; + case IkarusErrorInfo_Type_SubSystem_Filesystem: return "IkarusErrorType_SubSystem_Filesystem"; + case IkarusErrorInfo_Type_LibIkarus_InvalidState: return "IkarusErrorType_LibIkarus_InvalidState"; + case IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation: return "IkarusErrorType_LibIkarus_CannotPerformOperation"; + case IkarusErrorInfo_Type_LibIkarus_Timeout: return "IkarusErrorType_LibIkarus_Timeout"; + case IkarusErrorInfo_Type_Unknown: return "IkarusErrorType_Unknown"; + default: return "Unknown"; + } +} diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index 3d1c997..63ce726 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -21,39 +21,56 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - LOG_VERBOSE("project={}; name={}", project->path.c_str(), name); + auto ctx = project->function_context(); if (name == nullptr) { - LOG_ERROR("name is nullptr"); + ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return nullptr; } if (cppbase::is_empty_or_blank(name)) { - LOG_ERROR("name is empty or blank"); + ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return nullptr; } - VTRYRV(auto id, nullptr, project->db->transact([name](auto * db) -> cppbase::Result { - LOG_VERBOSE("creating blueprint in objects table"); + LOG_VERBOSE("project={}; name={}", project->path().c_str(), name); - TRY(db->execute( - "INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(IkarusObjectType_Blueprint), name - )); + VTRYRV( + auto id, + nullptr, + project->db() + ->transact([name](auto * db) -> cppbase::Result { + LOG_VERBOSE("creating blueprint in objects table"); - auto id = db->last_insert_rowid(); + TRY(db->execute( + "INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", + static_cast(IkarusObjectType_Blueprint), + name + )); - LOG_VERBOSE("blueprint is {}", id); + auto id = db->last_insert_rowid(); - LOG_VERBOSE("inserting blueprint into blueprints table"); + LOG_VERBOSE("id is {}", id); - TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?);", id)); + LOG_VERBOSE("inserting blueprint into blueprints table"); - return cppbase::ok(id); - })); + TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?);", id)); + + return cppbase::ok(id); + }) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("unable to insert blueprint into database: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); LOG_VERBOSE("successfully created blueprint"); - return new IkarusBlueprint{project, id}; + return project->get_blueprint(id); } void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { @@ -64,16 +81,27 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { return; } + auto * ctx = blueprint->project->function_context(); + LOG_VERBOSE("blueprint={}", blueprint->id); - if (auto res = blueprint->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id); res.is_err()) { - LOG_ERROR("failed to delete blueprint {} from objects table: {}", blueprint->id, res.unwrap_error()); - return; - } + TRYRV( + , + blueprint->project->db() + ->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to delete blueprint from objects table: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); - LOG_VERBOSE("blueprint was successfully deleted from database, freeing pointer"); + LOG_VERBOSE("blueprint was successfully deleted from database, freeing blueprint"); - delete blueprint; + blueprint->project->remove_blueprint(blueprint); LOG_VERBOSE("successfully deleted blueprint"); } @@ -86,14 +114,23 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { return 0; } + auto * ctx = blueprint->project->function_context(); + LOG_VERBOSE("blueprint={}", blueprint->id); VTRYRV( auto count, 0, - blueprint->project->db->query_one( - "SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint_id` = ?;", blueprint->id - ) + blueprint->project->db() + ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint_id` = ?;", blueprint->id) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint property count: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) ); return static_cast(count); @@ -109,6 +146,8 @@ void ikarus_blueprint_get_properties( return; } + auto * ctx = blueprint->project->function_context(); + if (properties_out == nullptr) { LOG_ERROR("properties_out is nullptr"); return; @@ -118,17 +157,28 @@ void ikarus_blueprint_get_properties( IkarusId ids[properties_out_size]; - if (auto res = blueprint->project->db->query_many_buffered( - "SELECT `id` FROM `properties` WHERE `source` = ?", static_cast(ids), properties_out_size, blueprint->id - ); - res.is_err()) { - LOG_ERROR("failed to fetch blueprint property ids: {}", res.unwrap_error()); - return; - } + TRYRV( + , + blueprint->project->db() + ->query_many_buffered( + "SELECT `id` FROM `properties` WHERE `source` = ?", + static_cast(ids), + properties_out_size, + blueprint->id + ) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint property ids: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); for (size_t i = 0; i < properties_out_size; ++i) { /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - properties_out[i] = new IkarusProperty{blueprint->project, ids[i]}; + properties_out[i] = blueprint->project->get_property(ids[i]); } LOG_VERBOSE("successfully fetched blueprint properties"); diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index 91b3d3e..28fb068 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -3,4 +3,7 @@ #include /// \private -struct IkarusEntity : public IkarusObject {}; +struct IkarusEntity : public IkarusObject { + inline IkarusEntity(struct IkarusProject * project, IkarusId id): + IkarusObject{project, id} {} +}; diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index db52508..5cbea00 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -1,11 +1,148 @@ +#pragma once + +#include #include +#include +#include #include #include +#include + +constexpr inline size_t MAXIMUM_ERROR_INFOS = 8; +constexpr inline size_t MAXIMUM_ERROR_MESSAGE_LENGTH = 256; + +class FunctionContext { +public: + explicit FunctionContext(struct IkarusProject * project); + FunctionContext(FunctionContext const&) noexcept = default; + FunctionContext(FunctionContext&&) noexcept = default; + + auto operator=(FunctionContext const&) noexcept -> FunctionContext& = default; + auto operator=(FunctionContext&&) noexcept -> FunctionContext& = default; + + ~FunctionContext(); + + template + requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) + auto set_error(std::string_view error_message, bool log_error, Infos... infos) -> void; + +private: + struct IkarusProject * _project; +}; + /// \private struct IkarusProject { - std::string name; - std::filesystem::path path; - std::unique_ptr db; +public: + [[nodiscard]] inline auto name() const -> std::string_view { + return _name; + } + + [[nodiscard]] inline auto path() const -> std::filesystem::path const& { + return _path; + } + + [[nodiscard]] inline auto db() -> sqlitecpp::Connection * { + return _db.get(); + } + + inline auto function_context() -> FunctionContext * { + return &_function_contexts.emplace(this); + } + + [[nodiscard]] IkarusBlueprint * get_blueprint(IkarusId id) { + return get_cached_object(id, this->_blueprints); + } + + auto remove_blueprint(IkarusBlueprint * blueprint) -> void { + remove_cached_object(blueprint, _blueprints); + } + + [[nodiscard]] auto get_entity(IkarusId id) -> IkarusEntity * { + return get_cached_object(id, this->_entities); + } + + auto remove_entity(IkarusEntity * entity) -> void { + remove_cached_object(entity, _entities); + } + + [[nodiscard]] auto get_property(IkarusId id) -> IkarusProperty * { + return get_cached_object(id, this->_properties); + } + + auto remove_property(IkarusProperty * property) -> void { + remove_cached_object(property, _properties); + } + +private: + template + [[nodiscard]] T * get_cached_object(IkarusId id, std::unordered_map>& cache) { + if (auto iter = cache.find(id); iter == cache.cend()) { + auto [ret_iter, _] = cache.emplace(id, std::make_unique(this, id)); + + return ret_iter->second.get(); + } else { + return iter->second.get(); + } + } + + template + void remove_cached_object(T * object, std::unordered_map>& cache) { + cache.erase(object->id); + } + +private: + friend class FunctionContext; + + std::string _name; + std::filesystem::path _path; + std::unique_ptr _db; + + std::array error_infos; + std::string error_message_buffer; + + std::unordered_map> _blueprints; + std::unordered_map> _properties; + std::unordered_map> _entities; + + std::stack _function_contexts; }; + +FunctionContext::FunctionContext(struct IkarusProject * project): + _project{project} {} + +FunctionContext::~FunctionContext() { + if (_project->_function_contexts.size() == 1) { + if (_project->error_message_buffer.empty()) { + _project->error_message_buffer.push_back('\0'); + } else { + _project->error_message_buffer[0] = '\0'; + } + + _project->error_infos = {}; + } + + _project->_function_contexts.pop(); +} + +template + requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) +auto FunctionContext::set_error(std::string_view error_message, bool log_error, Infos... infos) { + if (error_message.size() > _project->error_message_buffer.size()) { + _project->error_message_buffer.resize(error_message.size() + 1); + } + + for (int i = 0; i < error_message.size(); ++i) { + _project->error_message_buffer[i] = error_message[i]; + } + + _project->error_message_buffer[error_message.size()] = '\0'; + _project->error_infos = {infos...}; + + if (log_error) { + LOG_ERROR( + "Error({}): {}", fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), error_message + ); + } +} From 9e63219bf9b4c100cb2d7dfce43c2023b6df66c8 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 21 Nov 2023 15:10:43 +0100 Subject: [PATCH 013/166] split property & values into separate classes and files Signed-off-by: Folling --- implementation_details | 12 -- include/ikarus/objects/blueprint.h | 5 +- include/ikarus/objects/entity.h | 39 +++--- include/ikarus/objects/object.h | 12 ++ include/ikarus/objects/object_type.h | 3 - .../objects/properties/number_property.h | 25 ++++ .../objects/{ => properties}/property.h | 81 ++++++----- .../{ => properties}/property_source.h | 16 ++- .../objects/{ => properties}/property_type.h | 2 - .../settings/number_property_settings.h | 31 ++++ .../properties/settings/property_settings.h | 92 ++++++++++++ .../settings/text_property_settings.h | 31 ++++ .../settings/toggle_property_settings.h | 31 ++++ .../ikarus/objects/properties/text_property.h | 25 ++++ .../objects/properties/toggle_property.h | 25 ++++ include/ikarus/objects/property_type_info.h | 79 ----------- include/ikarus/values/number_value.h | 44 ++++++ include/ikarus/values/text_value.h | 44 ++++++ include/ikarus/values/toggle_value.h | 44 ++++++ include/ikarus/values/value.h | 132 ++++-------------- src/objects/blueprint.cpp | 3 - src/objects/property.cpp | 87 ++++++++++++ src/objects/property_info.hpp | 74 ++++++++++ src/objects/property_source.hpp | 10 ++ src/persistence/project.hpp | 3 +- src/values/number_value.hpp | 5 + src/values/text_value.hpp | 8 ++ src/values/toggle_value.hpp | 6 + 28 files changed, 700 insertions(+), 269 deletions(-) delete mode 100644 implementation_details create mode 100644 include/ikarus/objects/properties/number_property.h rename include/ikarus/objects/{ => properties}/property.h (62%) rename include/ikarus/objects/{ => properties}/property_source.h (69%) rename include/ikarus/objects/{ => properties}/property_type.h (96%) create mode 100644 include/ikarus/objects/properties/settings/number_property_settings.h create mode 100644 include/ikarus/objects/properties/settings/property_settings.h create mode 100644 include/ikarus/objects/properties/settings/text_property_settings.h create mode 100644 include/ikarus/objects/properties/settings/toggle_property_settings.h create mode 100644 include/ikarus/objects/properties/text_property.h create mode 100644 include/ikarus/objects/properties/toggle_property.h delete mode 100644 include/ikarus/objects/property_type_info.h create mode 100644 include/ikarus/values/number_value.h create mode 100644 include/ikarus/values/text_value.h create mode 100644 include/ikarus/values/toggle_value.h create mode 100644 src/objects/property.cpp create mode 100644 src/objects/property_info.hpp create mode 100644 src/objects/property_source.hpp create mode 100644 src/values/number_value.hpp create mode 100644 src/values/text_value.hpp create mode 100644 src/values/toggle_value.hpp diff --git a/implementation_details b/implementation_details deleted file mode 100644 index 42b38fc..0000000 --- a/implementation_details +++ /dev/null @@ -1,12 +0,0 @@ -This list is intended to help keep the documentation up to date. -If you make changes to, for example, templates, always check the documentation for templates. -But sometimes information is shared. and referenced in multiple places. This helps keep track of that. - -Usage: Search for these keys prefixed with IMPLEMENTATION_DETAIL_* to change documentation in relevant places. - -DATABASE: References to the usage of a database -TREE_LAYOUT: References to our usage of a tree layout -OBJECT_TYPES: References to the types of objects -OBJECT_SCOPES: References to the usage of object scopes -PROPERTY_TYPES: The property types that currently exist -LAZY_VALUE_CREATION: The fact that values are created lazily \ No newline at end of file diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 85f40eb..57d1a7c 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -77,7 +77,10 @@ IKA_API void ikarus_blueprint_get_linked_entities( /// \pre \li Must exist. /// \return The blueprint represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint const * blueprint); +IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint); + +/// \see ikarus_blueprint_to_object +IKA_API struct IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 813adea..2ac9087 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -50,12 +50,7 @@ struct IkarusEntity; /// \return The created entity or null if an error occurs. /// \remark Must be freed using #ikarus_free. IKA_API IkarusEntity * ikarus_entity_create( - struct IkarusProject * project, - struct IkarusEntityFolder * parent, - size_t position, - char const * name, - struct IkarusBlueprint ** blueprints, - size_t blueprints_count + struct IkarusProject * project, char const * name, struct IkarusBlueprint ** blueprints, size_t blueprints_count ); /// \brief Deletes an entity. @@ -67,16 +62,6 @@ IKA_API void ikarus_entity_delete(IkarusEntity * entity); IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint); -/// \brief Checks if an entity has a specific property. -/// \param entity The entity to check. -/// \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 exist. -/// \return True if the entity has the property, false otherwise. -IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property); - /// \brief Links an entity to a blueprint. /// \param entity The entity to link. /// \pre \li Must not be null. @@ -98,6 +83,16 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru /// \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); +/// \brief Checks if an entity has a specific property. +/// \param entity The entity to check. +/// \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 exist. +/// \return True if the entity has the property, false otherwise. +IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property); + /// \brief Gets the number of properties of an entity. /// \param entity The entity to get the number of properties of. /// \pre \li Must not be null. @@ -126,7 +121,7 @@ IKA_API void ikarus_entity_get_properties( /// \pre \li Must exist. /// \return The value of the property or null if the entity does not have the property or an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusEntityValue * get_value(IkarusEntity const * entity, struct IkarusProperty const * property); +IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property); /// \brief Sets the value of a property of an entity. /// \param entity The entity to set the value of. @@ -138,11 +133,10 @@ IKA_API struct IkarusEntityValue * get_value(IkarusEntity const * entity, struct /// \param value The new value of the property. /// \pre \li Must not be null. /// \pre \li Must be of the same type as the property. -/// \param validate_settings If set, this function fails not only if the type of the value is invalid, but also if it's not -/// valid under the properties settings. \see property.h +/// \pre \li Must be valid for the property's settings. /// \remark If the entity does not have the property, this function fails. IKA_API void ikarus_entity_set_value( - IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue * value, bool validate_settings + IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value ); /// \brief Casts an entity to an object. @@ -151,7 +145,10 @@ IKA_API void ikarus_entity_set_value( /// \pre \li Must exist. /// \return The entity represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity const * entity); +IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity); + +/// \see ikarus_entity_to_object +IKA_API struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity); IKARUS_END_HEADER diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index 256e8a0..a2cce6a 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -41,6 +41,18 @@ IKA_API void ikarus_object_visit( void * data ); +/// \see ikarus_object_visit +IKA_API void ikarus_object_visit_const( + IkarusObject const * object, + void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), + void (*property_visitor)(struct IkarusProperty const *, void *), + void (*entity_visitor)(struct IkarusEntity const *, void *), + void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder const *, void *), + void (*property_folder_visitor)(struct IkarusPropertyFolder const *, void *), + void (*entity_folder_visitor)(struct IkarusEntityFolder const *, void *), + void * data +); + IKARUS_END_HEADER // @} diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 73f724b..2539e2d 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -10,10 +10,7 @@ IKARUS_BEGIN_HEADER -// IMPLEMENTATION_DETAIL_OBJECT_TYPES - /// \brief The type of an object. -/// \remark The folder types are identical to their counterparts in #IkarusFolderType. enum IkarusObjectType { /// \brief Not an object or no object. IkarusObjectType_None = 0, diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h new file mode 100644 index 0000000..9045140 --- /dev/null +++ b/include/ikarus/objects/properties/number_property.h @@ -0,0 +1,25 @@ +#pragma once + +/// \file number_property.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// \brief Number properties store a numeric value. (e.g. "Weight" or "Age") +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusNumberProperty; + +IKA_API IkarusNumberProperty * ikarus_number_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + struct IkarusNumberSettings * settings +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/properties/property.h similarity index 62% rename from include/ikarus/objects/property.h rename to include/ikarus/objects/properties/property.h index a62a821..f1ebf49 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/properties/property.h @@ -1,12 +1,10 @@ #pragma once -// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION - /// \file property.h /// \author Folling #include -#include +#include #include /// \defgroup properties Properties @@ -52,33 +50,12 @@ IKARUS_BEGIN_HEADER /// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. struct IkarusProperty; -/// \brief Creates a property -/// \param project The project the property is part of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param property_source The property source the property is part of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param name The name of the property. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param property_info The info of the property. -/// \pre \li Must not be null. -/// \return The created property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusProperty * ikarus_property_create( - struct IkarusProject * project, - struct IkarusPropertySource * property_source, - char const * name, - struct IkarusPropertyTypeInfo * property_info -); - /// \brief Deletes a property. /// \param property The property to delete. /// \pre \li Must not be null. /// \pre \li Must exist. /// \remark The property must not be accessed after deletion. -IKA_API void ikarus_property_delete(struct IkarusProperty * property); +IKA_API void ikarus_property_delete(IkarusProperty * property); /// \brief Gets the type info of a property. /// \param property The property to get the type info of. @@ -86,7 +63,18 @@ IKA_API void ikarus_property_delete(struct IkarusProperty * property); /// \pre \li Must exist. /// \return The type info of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyTypeInfo * ikarus_property_get_type_info(IkarusProperty const * property); +IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property); + +/// \brief Gets the settings of a property. +/// \param property The property to get the settings of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The settings of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertySettings * ikarus_property_get_settings(IkarusProperty * property); + +/// \see ikarus_property_get_settings +IKA_API struct IkarusPropertySettings const * ikarus_property_get_settings_const(IkarusProperty const * property); /// \brief Gets the source of a property. /// \param property The property to get the source of. @@ -94,25 +82,31 @@ IKA_API struct IkarusPropertyTypeInfo * ikarus_property_get_type_info(IkarusProp /// \pre \li Must exist. /// \return The source of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * ikarus_property_get_source(IkarusProperty const * property); +IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property); -/// \brief Gets the default value of a property. -/// \param property The property to get the default value of. +/// \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. -/// \return The default value of the property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); +/// \param toggle_property The function to call if the property is a toggle property. Skipped if null. +/// \param number_property The function to call if the property is a number property. Skipped if null. +/// \param text_property The function to call if the property is a text property. Skipped if null. +/// \param data The data to pass to the functions. +IKA_API void ikarus_property_visit( + IkarusProperty * property, + void (*toggle_property)(struct IkarusToggleProperty *, void *), + void (*number_property)(struct IkarusNumberProperty *, void *), + void (*text_property)(struct IkarusTextProperty *, void *), + void * data +); -/// \brief Sets the type info of a property and resets all values to the new default value. -/// \param property The property to set the type info of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_type The new type info of the property. -/// \param attempt_conversion Whether to attempt to convert the property's values to the new type info. Conversion rules are -/// unspecified for now, but follow common sense. -IKA_API void ikarus_property_set_type_info( - IkarusProperty * property, struct IkarusPropertyTypeInfo new_type_info, bool attempt_conversion +/// \see ikarus_property_visit +IKA_API void ikarus_property_visit_const( + IkarusProperty const * property, + void (*toggle_property)(struct IkarusToggleProperty const *, void *), + void (*number_property)(struct IkarusNumberProperty const *, void *), + void (*text_property)(struct IkarusTextProperty const *, void *), + void * data ); /// \brief Casts a property to an object. @@ -121,7 +115,10 @@ IKA_API void ikarus_property_set_type_info( /// \pre \li Must exist. /// \return The property represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty const * property); +IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty * property); + +/// \see ikarus_property_to_object +IKA_API struct IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property); IKARUS_END_HEADER diff --git a/include/ikarus/objects/property_source.h b/include/ikarus/objects/properties/property_source.h similarity index 69% rename from include/ikarus/objects/property_source.h rename to include/ikarus/objects/properties/property_source.h index 4dbcd47..275baa4 100644 --- a/include/ikarus/objects/property_source.h +++ b/include/ikarus/objects/properties/property_source.h @@ -10,7 +10,7 @@ IKARUS_BEGIN_HEADER -struct PropertySource; +struct IkarusPropertySource; /// \brief Creates an blueprint property source. /// \param blueprint The blueprint to create the property source for. @@ -18,7 +18,7 @@ struct PropertySource; /// \pre \li Must exist. /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct PropertySource * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint); +IKA_API struct IkarusPropertySource * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint); /// \brief Creates an entity property source. /// \param entity The entity to create the property source for. @@ -26,7 +26,7 @@ IKA_API struct PropertySource * ikarus_property_source_create_blueprint(struct I /// \pre \li Must exist. /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct PropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity); +IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity); /// \brief Visits a property source, calling the appropriate callback. /// \param property_source The property source to visit. @@ -36,12 +36,20 @@ IKA_API struct PropertySource * ikarus_property_source_create_entity(struct Ikar /// \param entity_visitor The callback to call if the source is an entity, skipped if null. /// \param user_data User data to pass to the callbacks. IKA_API void ikarus_property_source_visit( - struct PropertySource * property_source, + struct IkarusPropertySource * property_source, void (*blueprint_visitor)(struct IkarusBlueprint *, void *), void (*entity_visitor)(struct IkarusEntity *, void *), void * user_data ); +/// \see ikarus_property_source_visit +IKA_API void ikarus_property_source_visit_const( + struct IkarusPropertySource const * property_source, + void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), + void (*entity_visitor)(struct IkarusEntity const *, void *), + void * user_data +); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/objects/property_type.h b/include/ikarus/objects/properties/property_type.h similarity index 96% rename from include/ikarus/objects/property_type.h rename to include/ikarus/objects/properties/property_type.h index e26b1f0..89e5fa1 100644 --- a/include/ikarus/objects/property_type.h +++ b/include/ikarus/objects/properties/property_type.h @@ -1,7 +1,5 @@ #pragma once -// IMPLEMENTATION_DETAIL_PROPERTY_TYPES - /// \file property_type.h /// \author Folling diff --git a/include/ikarus/objects/properties/settings/number_property_settings.h b/include/ikarus/objects/properties/settings/number_property_settings.h new file mode 100644 index 0000000..fcdaee8 --- /dev/null +++ b/include/ikarus/objects/properties/settings/number_property_settings.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file number_property_settings.h +/// \author Folling + +#include + +/// \addtogroup property_settings PropertySettings +/// \brief Number property settings add additional constraints to number properties. +/// \details The following settings are available: +/// +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusNumberPropertySettings; + +/// \brief Sets the default value for a number property. +/// \param settings The number property settings. +/// \pre \li Must not be null. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API void ikarus_number_property_settings_set_default_value( + struct IkarusNumberPropertySettings * settings, struct IkarusNumberValue * default_value +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/properties/settings/property_settings.h b/include/ikarus/objects/properties/settings/property_settings.h new file mode 100644 index 0000000..359097c --- /dev/null +++ b/include/ikarus/objects/properties/settings/property_settings.h @@ -0,0 +1,92 @@ +#pragma once + +/// \file property_settings.h +/// \author Folling + +#include + +/// \defgroup property_settings PropertySettings +/// \brief Property settings add additional constraints to properties. +/// \details Each property has a certain set of settings. The options available depend on the type of the property. +/// Settings can be changed after the property has been created. Examples of settings are: +/// Note that the default value must be set using the concrete subtypes to ascertain type correctness. +/// - Minimum: The minimum value for a number property. +/// - Matches: A regular expression that the value of a text property must match. +/// There are also some common settings, shared among all properties: +/// - Default Value (default: PropertyType's default default (sic) value): The value that is returned if no value is specified +/// for some entity. +/// - List (default: false): If set to true, the property becomes a list. Instead of one number, you could then specify a series +/// of numbers. Note that each element in the list is subject to changes in values. E.g. when we say that all values are changed +/// in some way (e.g. reset to the default value), this applies to all elements in the list. +/// - Allow undefined (default: false): If set to true, you can specify an "unknown" value for a property. +/// It might not be known if a character is dead or not for example. +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusPropertySettings; + +/// \brief Gets the default value of a property. +/// \param settings The settings to get the default value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The default value of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusValue const * ikarus_property_settings_get_default_value(IkarusPropertySettings const * settings); + +/// \brief Fetches whether a property is a list. +/// \param settings The property settings. +/// \pre \li Must not be null. +/// \return True if the property is a list, false otherwise. +IKA_API bool ikarus_property_settings_is_list(IkarusPropertySettings const * settings); + +/// \brief Sets whether a property is a list. +/// \details Noop if unchanged. A change from list -> single truncates to just the first element. A change from single -> list +/// sets the first element to the current value. +/// \param settings The property settings. +/// \pre \li Must not be null. +/// \param list Whether the property should be +/// a list. +IKA_API void ikarus_property_settings_set_is_list(IkarusPropertySettings * settings, bool list); + +/// \brief Fetches whether a property may be undefined. +/// \param settings The property settings. +/// \pre \li Must not be null. +/// \return True if the property may be undefined, false otherwise. +IKA_API bool ikarus_property_settings_may_be_undefined(IkarusPropertySettings const * settings); + +/// \brief Sets whether a property may be undefined. +/// \details Noop if unchanged. If the transition is from undefined -> defined, all undefined values will be reset to the +/// default value. +/// \param settings The property settings. +/// \pre \li Must not be null. +/// \param allow_undefined Whether the property should be allowed to be undefined. +/// \param allow_undefined Whether the property should be allowed to be undefined. +IKA_API void ikarus_property_settings_set_may_be_undefined(IkarusPropertySettings * settings, bool allow_undefined); + +/// \brief Visits a property settings. Calling the appropriate function for the property's type. +/// \param settings The property settings. +/// \pre \li Must not be null. +/// \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 Data passed to the visitors. +IKA_API void ikarus_property_settings_visit( + struct IkarusPropertySettings * settings, + void (*toggle_property_visitor)(struct IkarusTogglePropertySettings * settings, void * data), + void (*number_property_visitor)(struct IkarusNumberPropertySettings * settings, void * data), + void (*text_property_visitor)(struct IkarusTextPropertySettings * settings, void * data), + void * data +); + +IKA_API void ikarus_property_settings_visit_const( + struct IkarusPropertySettings const * settings, + void (*toggle_property_visitor)(struct IkarusTogglePropertySettings const * settings, void * data), + void (*number_property_visitor)(struct IkarusNumberPropertySettings const * settings, void * data), + void (*text_property_visitor)(struct IkarusTextPropertySettings const * settings, void * data), + void * data +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/properties/settings/text_property_settings.h b/include/ikarus/objects/properties/settings/text_property_settings.h new file mode 100644 index 0000000..926058d --- /dev/null +++ b/include/ikarus/objects/properties/settings/text_property_settings.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file text_property_settings.h +/// \author Folling + +#include + +/// \addtogroup property_settings PropertySettings +/// \brief Text property settings add additional constraints to text properties. +/// \details The following settings are available: +/// +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusTextPropertySettings; + +/// \brief Sets the default value for a text property. +/// \param settings The text property settings. +/// \pre \li Must not be null. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API void ikarus_text_property_settings_set_default_value( + struct IkarusTextPropertySettings * settings, struct IkarusTextValue * default_value +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/properties/settings/toggle_property_settings.h b/include/ikarus/objects/properties/settings/toggle_property_settings.h new file mode 100644 index 0000000..d372929 --- /dev/null +++ b/include/ikarus/objects/properties/settings/toggle_property_settings.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file toggle_property_settings.h +/// \author Folling + +#include + +/// \addtogroup property_settings PropertySettings +/// \brief Toggle property settings add additional constraints to toggle properties. +/// \details The following settings are available: +/// +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusTogglePropertySettings; + +/// \brief Sets the default value for a toggle property. +/// \param settings The toggle property settings. +/// \pre \li Must not be null. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API void ikarus_toggle_property_settings_set_default_value( + struct IkarusTogglePropertySettings * settings, struct IkarusToggleValue * default_value +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h new file mode 100644 index 0000000..f7975b3 --- /dev/null +++ b/include/ikarus/objects/properties/text_property.h @@ -0,0 +1,25 @@ +#pragma once + +/// \file text_property.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// \brief Text properties store an arbitrary piece of text. (e.g. "Firstname" or "Description") +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusTextProperty; + +IKA_API IkarusTextProperty * ikarus_text_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + struct IkarusTextSettings * settings +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h new file mode 100644 index 0000000..baa0801 --- /dev/null +++ b/include/ikarus/objects/properties/toggle_property.h @@ -0,0 +1,25 @@ +#pragma once + +/// \file toggle_property.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// \brief Toggle properties store a value that can be either true or false. (e.g. "Is the character dead?") +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusToggleProperty; + +IKA_API IkarusToggleProperty * ikarus_toggle_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + struct IkarusToggleSettings * settings +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/property_type_info.h b/include/ikarus/objects/property_type_info.h deleted file mode 100644 index cd7b6b9..0000000 --- a/include/ikarus/objects/property_type_info.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -/// \file property_info.h -/// \author Folling - -#include - -/// \addtogroup properties Properties -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief Information about a property. -/// \details Property information includes their type and settings consolidated to ascertain type safety. -struct IkarusPropertyTypeInfo; - -/// \brief Information about a toggle property. -struct IkarusTogglePropertyInfo; - -/// \brief Information about a number property. -struct IkarusNumberPropertyInfo; - -/// \brief Information about a text property. -struct IkarusTextPropertyInfo; - -/// \brief Creates a new toggle property info. -/// \return The created toggle property info. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTogglePropertyInfo * ikarus_toggle_property_info_create(); -/// \brief Sets the default value of a toggle property info. -/// \param toggle_property_info The toggle property info to set the default value of. -/// \pre \li Must not be null. -/// \param default_value The default value to set. -/// \pre \li Must not be null. -IKA_API void ikarus_toggle_property_info_set_default_value( - IkarusTogglePropertyInfo * toggle_property_info, struct IkarusToggleValue * default_value -); -/// \brief Converts a toggle property info to a generic property info. -/// \param toggle_property_info The toggle property info to convert. -/// \return The converted property info. -IKA_API IkarusPropertyTypeInfo * ikarus_toggle_property_info_to_property_info(IkarusTogglePropertyInfo * toggle_property_info); - -/// \brief Creates a new number property info. -/// \return The created number property info. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberPropertyInfo * ikarus_number_property_info_create(); -/// \brief Sets the default value of a number property info. -/// \param number_property_info The number property info to set the default value of. -/// \pre \li Must not be null. -/// \param default_value The default value to set. -/// \pre \li Must not be null. -IKA_API void ikarus_number_property_info_set_default_value( - IkarusNumberPropertyInfo * number_property_info, struct IkarusNumberValue * default_value -); -/// \brief Converts a number property info to a generic property info. -/// \param number_property_info The number property info to convert. -/// \return The converted property info. -IKA_API IkarusPropertyTypeInfo * ikarus_number_property_info_to_property_info(IkarusNumberPropertyInfo * number_property_info); - -/// \brief Creates a new text property info. -/// \return The created text property info. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextPropertyInfo * ikarus_text_property_info_create(); -/// \brief Sets the default value of a text property info. -/// \param text_property_info The text property info to set the default value of. -/// \pre \li Must not be null. -/// \param default_value The default value to set. -/// \pre \li Must not be null. -IKA_API void ikarus_text_property_info_set_default_value( - IkarusTextPropertyInfo * text_property_info, struct IkarusTextValue * default_value -); -/// \brief Converts a text property info to a generic property info. -/// \param text_property_info The text property info to convert. -/// \return The converted property info. -IKA_API IkarusPropertyTypeInfo * ikarus_text_property_info_to_property_info(IkarusTextPropertyInfo * text_property_info); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h new file mode 100644 index 0000000..2c054f8 --- /dev/null +++ b/include/ikarus/values/number_value.h @@ -0,0 +1,44 @@ +#pragma once + +/// \file number_value.h +/// \author Folling + +#include + +/// \addtogroup entity_value Entity Values +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A true/false boolean-like value. For example "IsDead". +struct IkarusNumberValue; + +/// \brief Creates a number value from a long double. +/// \param value The numeric value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberValue * ikarus_number_value_create(long double value); + +/// \brief Creates an indeterminate number value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); + +/// \brief Sets the value of a number value. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); + +/// \brief Fetches the underlying value of a number value. +/// \param value The number value. +/// \return The underlying value. +/// \warning If the value is indeterminate, false is returned. +IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value); + +/// \brief Converts a number value to an entity value. +/// \param number_value The number value to convert. +/// \return The converted entity value. +IKA_API struct IkarusEntityValue * ikarus_number_value_to_entity_value(IkarusNumberValue * number_value); + +IKARUS_END_HEADER diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h new file mode 100644 index 0000000..867a638 --- /dev/null +++ b/include/ikarus/values/text_value.h @@ -0,0 +1,44 @@ +#pragma once + +/// \file text_value.h +/// \author Folling + +#include + +/// \addtogroup entity_value Entity Values +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A true/false boolean-like value. For example "IsDead". +struct IkarusTextValue; + +/// \brief Creates a text value from a boolean. +/// \param value The text value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextValue * ikarus_text_value_create(char const * value); + +/// \brief Creates an indeterminate text value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); + +/// \brief Sets the value of a text value. +/// \param value The text value. +/// \pre \li Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); + +/// \brief Fetches the underlying value of a text value. +/// \param value The text value. +/// \return The underlying value. +/// \warning If the value is indeterminate, false is returned. +IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); + +/// \brief Converts a text value to an entity value. +/// \param text_value The text value to convert. +/// \return The converted entity value. +IKA_API struct IkarusEntityValue * ikarus_text_value_to_entity_value(IkarusTextValue * text_value); + +IKARUS_END_HEADER diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h new file mode 100644 index 0000000..7c77870 --- /dev/null +++ b/include/ikarus/values/toggle_value.h @@ -0,0 +1,44 @@ +#pragma once + +/// \file toggle_value.h +/// \author Folling + +#include + +/// \addtogroup entity_value Entity Values +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A true/false boolean-like value. For example "IsDead". +struct IkarusToggleValue; + +/// \brief Creates a toggle value from a boolean. +/// \param value The toggle value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value); + +/// \brief Creates an indeterminate toggle value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); + +/// \brief Sets the value of a toggle value. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); + +/// \brief Fetches the underlying value of a toggle value. +/// \param value The toggle value. +/// \return The underlying value. +/// \warning If the value is indeterminate, false is returned. +IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value); + +/// \brief Converts a toggle value to an entity value. +/// \param toggle_value The toggle value to convert. +/// \return The converted entity value. +IKA_API struct IkarusEntityValue * ikarus_toggle_value_to_entity_value(IkarusToggleValue * toggle_value); + +IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index a30cf2d..fd17f5b 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -1,16 +1,11 @@ #pragma once -// IMPLEMENTATION_DETAIL_PROPERTY_TYPES - /// \file value.h /// \author Folling #include -#include #include -IKARUS_BEGIN_HEADER - /// \defgroup entity_value Entity Values /// \brief The values stored in entities. /// \details Each entity has a value for each property it is associated with. @@ -19,112 +14,47 @@ IKARUS_BEGIN_HEADER /// \see PropertyType PropertySettings /// @{ -/// \brief A true/false boolean-like value. For example "IsDead". -struct IkarusToggleValue; +IKARUS_BEGIN_HEADER -/// \brief An arbitrary numeric value. For example "Age". -struct IkarusNumberValue; +/// \brief The common type for all values. +struct IkarusValue; -/// \brief An arbitrary textual value. For example "First Name". -struct IkarusTextValue; - -/// \brief The value of an entity associated with a property. -struct IkarusEntityValue; - -/// \brief Creates a toggle value from a boolean. -/// \param value The toggle value. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value); -/// \brief Creates an indeterminate toggle value. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); -/// \brief Sets the value of a toggle value. -/// \param value The toggle value. -/// \pre Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); - -/// \brief Creates a number value from a number. -/// \param value The number value. -/// \pre Must be finite & not NaN. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create(long double value); -/// \brief Creates an indeterminate number value. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); -/// \brief Sets the value of a number value. -/// \param value The number value. -/// \pre Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); - -/// \brief Creates a text value from string. -/// \param value The text value. -/// \pre Must not be null. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create(char const * value); -/// \brief Creates an indeterminate text value. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); -/// \brief Sets the value of a text value. -/// \param value The text value. -/// \pre Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); - -/// \brief Checks if a toggle value is indeterminate. -/// \param value The toggle value. -/// \pre Must not be null. +/// \brief Checks if a value is indeterminate. +/// \param value The value. +/// \pre \li Must not be null. /// \return True if the value is indeterminate, false otherwise. -IKA_API bool ikarus_toggle_value_is_indeterminate(IkarusToggleValue const * value); -/// \brief Checks if a number value is indeterminate. -/// \param value The number value. -/// \pre Must not be null. -/// \return True if the value is indeterminate, false otherwise. -IKA_API bool ikarus_number_value_is_indeterminate(IkarusNumberValue const * value); -/// \brief Checks if a text value is indeterminate. -/// \param value The text value. -/// \pre Must not be null. -/// \return True if the value is indeterminate, false otherwise. -IKA_API bool ikarus_text_value_is_indeterminate(IkarusTextValue const * value); +IKA_API bool ikarus_value_is_indeterminate(IkarusValue const * value); -/// \brief Fetches the underlying value of a toggle value. -/// \param value The toggle value. -/// \return The underlying value. -/// \warning If the value is indeterminate, false is returned. -IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value); - -/// \brief Fetches the underlying value of a number value. -/// \param value The number value. -/// \return The underlying value. -/// \warning If the value is indeterminate, 0.0 is returned. -IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value); - -/// \brief Fetches the underlying value of a text value. -/// \param value The text value. -/// \return A copy of the underlying value. -/// \remark The returned value is a copy and owned by the caller. -/// \warning If the value is indeterminate, an empty string is returned. -IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); +// \brief Converts an entity value to a string. +// \pre \li Must not be null. +// \param value The entity value. +// \return A string representation of the value or null if an error occurred. +// \remark The returned value is a copy and owned by the caller. +IKA_API char const * ikarus_value_to_string(IkarusValue const * value); /// \brief Visits an entity value, calling the appropriate function for the value's type. /// \param value The entity value to visit. -/// \param toggle The function to call if the value is a toggle value. Skipped if null. -/// \param number The function to call if the value is a number value. Skipped if null. -/// \param text The function to call if the value is a text value. Skipped if null. +/// \param toggle_visitor The function to call if the value is a toggle value. Skipped if null. +/// \param number_visitor The function to call if the value is a number value. Skipped if null. +/// \param text_visitor The function to call if the value is a text value. Skipped if null. /// \param data The data passed to the visitor functions. IKA_API void ikarus_value_visit( - IkarusEntityValue * value, - void (*toggle)(IkarusToggleValue *, void *), - void (*number)(IkarusNumberValue *, void *), - void (*text)(IkarusTextValue *, void *), + IkarusValue * value, + void (*toggle_visitor)(struct IkarusToggleValue *, void *), + void (*number_visitor)(struct IkarusNumberValue *, void *), + void (*text_visitor)(struct IkarusTextValue *, void *), + void * data +); + +/// \see ikarus_value_visit +IKA_API void ikarus_value_visit_const( + IkarusValue const * value, + void (*toggle_visitor)(struct IkarusToggleValue const *, void *), + void (*number_visitor)(struct IkarusNumberValue const *, void *), + void (*text_visitor)(struct IkarusTextValue const *, void *), void * data ); IKARUS_END_HEADER + +/// @} diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index 63ce726..df059ac 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -5,10 +5,7 @@ #include #include -#include -#include -#include #include #include #include diff --git a/src/objects/property.cpp b/src/objects/property.cpp new file mode 100644 index 0000000..ba11491 --- /dev/null +++ b/src/objects/property.cpp @@ -0,0 +1,87 @@ +#include "property.hpp" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +IkarusProperty * ikarus_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + struct IkarusPropertyInfo * property_info +) { + LOG_INFO("creating new property"); + + if (project == nullptr) { + LOG_ERROR("project is nullptr"); + return nullptr; + } + + auto ctx = project->function_context(); + + if (property_source == nullptr) { + ctx->set_error("property_source is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return nullptr; + } + + if (name == nullptr) { + ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return nullptr; + } + + if (cppbase::is_empty_or_blank(name)) { + ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return nullptr; + } + + if (property_info == nullptr) { + ctx->set_error("property_info is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return nullptr; + } + + LOG_VERBOSE( + "project={}; name={}; property_source={}; property_info={}", + project->path().c_str(), + name, + std::visit( + cppbase::overload{ + [](IkarusBlueprint * blueprint) { return fmt::format("Blueprint({})", blueprint->id); }, + [](IkarusEntity * entity) { return fmt::format("Entity({})", entity->id); }}, + property_source->data + ), + std::visit( + cppbase::overload{ + [](IkarusTogglePropertyInfo * info) { + return fmt::format( + "Toggle(default_value={})", + cppbase::OwningString{ikarus_value_to_string(ikarus_toggle_value_to_entity_value(info->default_value))} + .data + ); + }, + [](IkarusNumberPropertyInfo * info) { + return fmt::format( + "Number(default_value={})", + cppbase::OwningString{ikarus_value_to_string(ikarus_number_value_to_entity_value(info->default_value))} + .data + ); + }, + [](IkarusTextPropertyInfo * info) { + return fmt::format( + "Text(default_value={})", + cppbase::OwningString{ikarus_value_to_string(ikarus_text_value_to_entity_value(info->default_value))} + .data + ); + }}, + property_info->data + ) + ); +} diff --git a/src/objects/property_info.hpp b/src/objects/property_info.hpp new file mode 100644 index 0000000..8c131f7 --- /dev/null +++ b/src/objects/property_info.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +// this looks a bit cursed, but there's reason to my madness: +// Let's go over the facts: +// 1. The client uses the concrete types (e.g. Ikarus"Toggle"PropertyInfo) +// 2. The API needs to accept a common type (i.e. IkarusPropertyInfo) +// 3. Casting between a concrete subtype and a common type is only defined behaviour if we use inheritance, otherwise an +// allocation is unavoidable +// 4. On the implementation side, we need to be able to distinguish between the concrete types +// +// There's a few ways to model this (using dynamic_casts, using an enum, a union, the visitor pattern, ...) but std::variant +// gives us the most type safety without any performance overhead + +/// \private +struct IkarusPropertyInfo { +public: + using IkarusPropertyInfoData = + std::variant; + +public: + inline explicit IkarusPropertyInfo( + std::variant data + ): + data{data} {} + +public: + [[nodiscard]] inline IkarusPropertyInfoData const& get_data() const { + return data; + } + +private: + IkarusPropertyInfoData data; +}; + +/// \private +struct IkarusTogglePropertyInfo : public IkarusPropertyInfo { +public: + inline IkarusTogglePropertyInfo(): + IkarusPropertyInfo{this} {} + +public: + [[nodiscard]] IkarusToggleValue * get_default_value() const {} + +private: + IkarusToggleValue * default_value{nullptr}; +}; + +/// \private +struct IkarusNumberPropertyInfo : public IkarusPropertyInfo { +public: + inline IkarusNumberPropertyInfo(): + IkarusPropertyInfo{this} {} + +private: + IkarusNumberValue * default_value{nullptr}; +}; + +/// \private +struct IkarusTextPropertyInfo : public IkarusPropertyInfo { +public: + inline IkarusTextPropertyInfo(): + IkarusPropertyInfo{this} {} + +private: + IkarusTextValue * default_value{nullptr}; +}; diff --git a/src/objects/property_source.hpp b/src/objects/property_source.hpp new file mode 100644 index 0000000..16fab4f --- /dev/null +++ b/src/objects/property_source.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include + +/// \private +struct IkarusPropertySource { + std::variant data; +}; diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index 5cbea00..fbb84f2 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -13,6 +13,7 @@ constexpr inline size_t MAXIMUM_ERROR_INFOS = 8; constexpr inline size_t MAXIMUM_ERROR_MESSAGE_LENGTH = 256; +/// \private class FunctionContext { public: explicit FunctionContext(struct IkarusProject * project); @@ -128,7 +129,7 @@ FunctionContext::~FunctionContext() { template requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) -auto FunctionContext::set_error(std::string_view error_message, bool log_error, Infos... infos) { +auto FunctionContext::set_error(std::string_view error_message, bool log_error, Infos... infos) -> void { if (error_message.size() > _project->error_message_buffer.size()) { _project->error_message_buffer.resize(error_message.size() + 1); } diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp new file mode 100644 index 0000000..51020fc --- /dev/null +++ b/src/values/number_value.hpp @@ -0,0 +1,5 @@ +#pragma once + +struct IkarusNumberValue { + long double value; +}; diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp new file mode 100644 index 0000000..20880c4 --- /dev/null +++ b/src/values/text_value.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +/// \private +struct IkarusTextValue { + std::string value; +}; diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp new file mode 100644 index 0000000..a2f2d70 --- /dev/null +++ b/src/values/toggle_value.hpp @@ -0,0 +1,6 @@ +#pragma once + +/// \private +struct IkarusToggleValue { + bool value; +}; From 6d26da6971188af07d805de97da071776852c315 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 21 Nov 2023 15:11:42 +0100 Subject: [PATCH 014/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 806c264..538616d 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 806c26457c4e9e3d613e63a8511150390c1b196d +Subproject commit 538616d8ced2d3f04659261ccae3b039d65da004 From fec84033af2056c3cef9650131ebb3d3faabbcdb Mon Sep 17 00:00:00 2001 From: Folling Date: Fri, 24 Nov 2023 14:52:46 +0100 Subject: [PATCH 015/166] intermediate commit Signed-off-by: Folling --- include/ikarus/objects/object_type.h | 14 +- .../properties/settings/property_settings.h | 1 + src/id.cpp | 8 +- src/id.hpp | 2 + src/objects/blueprint.cpp | 132 ++++++++++++++++-- src/objects/property.cpp | 74 ---------- src/objects/property_info.hpp | 74 ---------- src/objects/property_source.hpp | 2 +- .../migrations/m1_initial_layout.sql | 4 +- src/values/value.cpp | 31 ++++ src/values/value.hpp | 8 ++ 11 files changed, 175 insertions(+), 175 deletions(-) delete mode 100644 src/objects/property_info.hpp create mode 100644 src/values/value.cpp create mode 100644 src/values/value.hpp diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 2539e2d..578919a 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -14,18 +14,12 @@ IKARUS_BEGIN_HEADER enum IkarusObjectType { /// \brief Not an object or no object. IkarusObjectType_None = 0, - /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 0b00000001, - /// \brief An IkarusProperty. - IkarusObjectType_Property = 0b00000010, /// \brief An IkarusEntity. IkarusObjectType_Entity = 0b00000011, - /// \brief An IkarusBlueprintFolder - IkarusObjectType_BlueprintFolder = 0b01000001, - /// \brief An IkarusPropertyFolder - IkarusObjectType_PropertyFolder = 0b01000010, - /// \brief An IkarusEntityFolder - IkarusObjectType_EntityFolder = 0b01000011, + /// \brief An IkarusProperty. + IkarusObjectType_Property = 0b00000010, + /// \brief An IkarusBlueprint. + IkarusObjectType_Blueprint = 0b00000001, }; IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/settings/property_settings.h b/include/ikarus/objects/properties/settings/property_settings.h index 359097c..a47541e 100644 --- a/include/ikarus/objects/properties/settings/property_settings.h +++ b/include/ikarus/objects/properties/settings/property_settings.h @@ -79,6 +79,7 @@ IKA_API void ikarus_property_settings_visit( void * data ); +/// \see ikarus_property_settings_visit IKA_API void ikarus_property_settings_visit_const( struct IkarusPropertySettings const * settings, void (*toggle_property_visitor)(struct IkarusTogglePropertySettings const * settings, void * data), diff --git a/src/id.cpp b/src/id.cpp index cc3913f..7ec6491 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -7,12 +7,12 @@ uint64_t const IKARUS_ID_OBJECT_TYPE_BITS = 8; uint64_t const IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; -auto ikarus_id_get_object_type(IkarusId id) -> IkarusObjectType { - return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); +auto from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { + return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); } -auto ikarus_id_is_equal(IkarusId left, IkarusId right) -> bool { - return left == right; +auto ikarus_id_get_object_type(IkarusId id) -> IkarusObjectType { + return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); } TEST_CASE("id_object_type", "[id]") { diff --git a/src/id.hpp b/src/id.hpp index 4499c17..fc61b05 100644 --- a/src/id.hpp +++ b/src/id.hpp @@ -30,6 +30,8 @@ IKARUS_BEGIN_HEADER /// - last 56 bits: incremented counter generated by the database using IkarusId = int64_t; +IKA_API IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type); + /// \brief Fetches the object type of the given id. /// \param id The id to fetch the object type for. /// \return The object type of the given id. diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index df059ac..d6a38b1 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -1,4 +1,4 @@ -#include "blueprint.hpp" +#include #include #include @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -18,7 +19,7 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - auto ctx = project->function_context(); + auto * ctx = project->function_context(); if (name == nullptr) { ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); @@ -30,7 +31,7 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - LOG_VERBOSE("project={}; name={}", project->path().c_str(), name); + LOG_DEBUG("project={}; name={}", project->path().c_str(), name); VTRYRV( auto id, @@ -45,9 +46,9 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c name )); - auto id = db->last_insert_rowid(); + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - LOG_VERBOSE("id is {}", id); + LOG_DEBUG("blueprint is {}", id); LOG_VERBOSE("inserting blueprint into blueprints table"); @@ -80,7 +81,7 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { auto * ctx = blueprint->project->function_context(); - LOG_VERBOSE("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->id); TRYRV( , @@ -113,13 +114,13 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { auto * ctx = blueprint->project->function_context(); - LOG_VERBOSE("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->id); VTRYRV( auto count, 0, blueprint->project->db() - ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint_id` = ?;", blueprint->id) + ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->id) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch blueprint property count: {}", err), @@ -130,6 +131,10 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { }) ); + LOG_DEBUG("blueprint property count: {}", count); + + LOG_VERBOSE("successfully fetched blueprint property count"); + return static_cast(count); } @@ -146,11 +151,11 @@ void ikarus_blueprint_get_properties( auto * ctx = blueprint->project->function_context(); if (properties_out == nullptr) { - LOG_ERROR("properties_out is nullptr"); + ctx->set_error("properties_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return; } - LOG_VERBOSE("blueprint={}; properties_out_size={}", blueprint->id, properties_out_size); + LOG_DEBUG("blueprint={}; properties_out_size={}", blueprint->id, properties_out_size); IkarusId ids[properties_out_size]; @@ -173,6 +178,8 @@ void ikarus_blueprint_get_properties( }) ); + LOG_DEBUG("blueprint properties: [{}]", fmt::join(ids, ids + properties_out_size, ", ")); + for (size_t i = 0; i < properties_out_size; ++i) { /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) properties_out[i] = blueprint->project->get_property(ids[i]); @@ -180,3 +187,108 @@ void ikarus_blueprint_get_properties( LOG_VERBOSE("successfully fetched blueprint properties"); } + +size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { + LOG_VERBOSE("fetching blueprint linked entity count"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return 0; + } + + auto * ctx = blueprint->project->function_context(); + + LOG_DEBUG("blueprint={}", blueprint->id); + + VTRYRV( + auto count, + 0, + blueprint->project->db() + ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->id) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint linked entity count: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_DEBUG("blueprint linked entity count: {}", count); + + LOG_VERBOSE("successfully fetched blueprint linked entity count: {}", count); + + return static_cast(count); +} + +void ikarus_blueprint_get_linked_entities( + IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size +) { + LOG_VERBOSE("fetching blueprint linked entities"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return; + } + + auto * ctx = blueprint->project->function_context(); + + if (entities_out == nullptr) { + ctx->set_error("entities_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return; + } + + LOG_DEBUG("blueprint={}; entities_out_size={}", blueprint->id, entities_out_size); + + IkarusId ids[entities_out_size]; + + TRYRV( + , + blueprint->project->db() + ->query_many_buffered( + "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", + static_cast(ids), + entities_out_size, + blueprint->id + ) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint linked entity ids: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_DEBUG("blueprint linked entities: [{}]", fmt::join(ids, ids + entities_out_size, ", ")); + + for (size_t i = 0; i < entities_out_size; ++i) { + /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + entities_out[i] = blueprint->project->get_entity(ids[i]); + } + + LOG_VERBOSE("successfully fetched blueprint linked entities"); +} + +IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint) { + return const_cast(ikarus_blueprint_to_object_const(blueprint)); +} + +IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint) { + LOG_VERBOSE("casting blueprint to object"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return nullptr; + } + + // auto * ctx = blueprint->project->function_context(); + + LOG_DEBUG("blueprint={}", blueprint->id); + + LOG_VERBOSE("successfully casted blueprint to object"); + + return static_cast(blueprint); +} diff --git a/src/objects/property.cpp b/src/objects/property.cpp index ba11491..80d9031 100644 --- a/src/objects/property.cpp +++ b/src/objects/property.cpp @@ -9,79 +9,5 @@ #include #include #include -#include #include #include - -IkarusProperty * ikarus_property_create( - struct IkarusProject * project, - char const * name, - struct IkarusPropertySource * property_source, - struct IkarusPropertyInfo * property_info -) { - LOG_INFO("creating new property"); - - if (project == nullptr) { - LOG_ERROR("project is nullptr"); - return nullptr; - } - - auto ctx = project->function_context(); - - if (property_source == nullptr) { - ctx->set_error("property_source is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - if (name == nullptr) { - ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - if (cppbase::is_empty_or_blank(name)) { - ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - if (property_info == nullptr) { - ctx->set_error("property_info is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - LOG_VERBOSE( - "project={}; name={}; property_source={}; property_info={}", - project->path().c_str(), - name, - std::visit( - cppbase::overload{ - [](IkarusBlueprint * blueprint) { return fmt::format("Blueprint({})", blueprint->id); }, - [](IkarusEntity * entity) { return fmt::format("Entity({})", entity->id); }}, - property_source->data - ), - std::visit( - cppbase::overload{ - [](IkarusTogglePropertyInfo * info) { - return fmt::format( - "Toggle(default_value={})", - cppbase::OwningString{ikarus_value_to_string(ikarus_toggle_value_to_entity_value(info->default_value))} - .data - ); - }, - [](IkarusNumberPropertyInfo * info) { - return fmt::format( - "Number(default_value={})", - cppbase::OwningString{ikarus_value_to_string(ikarus_number_value_to_entity_value(info->default_value))} - .data - ); - }, - [](IkarusTextPropertyInfo * info) { - return fmt::format( - "Text(default_value={})", - cppbase::OwningString{ikarus_value_to_string(ikarus_text_value_to_entity_value(info->default_value))} - .data - ); - }}, - property_info->data - ) - ); -} diff --git a/src/objects/property_info.hpp b/src/objects/property_info.hpp deleted file mode 100644 index 8c131f7..0000000 --- a/src/objects/property_info.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include -#include - -// this looks a bit cursed, but there's reason to my madness: -// Let's go over the facts: -// 1. The client uses the concrete types (e.g. Ikarus"Toggle"PropertyInfo) -// 2. The API needs to accept a common type (i.e. IkarusPropertyInfo) -// 3. Casting between a concrete subtype and a common type is only defined behaviour if we use inheritance, otherwise an -// allocation is unavoidable -// 4. On the implementation side, we need to be able to distinguish between the concrete types -// -// There's a few ways to model this (using dynamic_casts, using an enum, a union, the visitor pattern, ...) but std::variant -// gives us the most type safety without any performance overhead - -/// \private -struct IkarusPropertyInfo { -public: - using IkarusPropertyInfoData = - std::variant; - -public: - inline explicit IkarusPropertyInfo( - std::variant data - ): - data{data} {} - -public: - [[nodiscard]] inline IkarusPropertyInfoData const& get_data() const { - return data; - } - -private: - IkarusPropertyInfoData data; -}; - -/// \private -struct IkarusTogglePropertyInfo : public IkarusPropertyInfo { -public: - inline IkarusTogglePropertyInfo(): - IkarusPropertyInfo{this} {} - -public: - [[nodiscard]] IkarusToggleValue * get_default_value() const {} - -private: - IkarusToggleValue * default_value{nullptr}; -}; - -/// \private -struct IkarusNumberPropertyInfo : public IkarusPropertyInfo { -public: - inline IkarusNumberPropertyInfo(): - IkarusPropertyInfo{this} {} - -private: - IkarusNumberValue * default_value{nullptr}; -}; - -/// \private -struct IkarusTextPropertyInfo : public IkarusPropertyInfo { -public: - inline IkarusTextPropertyInfo(): - IkarusPropertyInfo{this} {} - -private: - IkarusTextValue * default_value{nullptr}; -}; diff --git a/src/objects/property_source.hpp b/src/objects/property_source.hpp index 16fab4f..e2a2739 100644 --- a/src/objects/property_source.hpp +++ b/src/objects/property_source.hpp @@ -2,7 +2,7 @@ #include -#include +#include /// \private struct IkarusPropertySource { diff --git a/src/persistence/migrations/m1_initial_layout.sql b/src/persistence/migrations/m1_initial_layout.sql index 7b49cb6..d94fa10 100644 --- a/src/persistence/migrations/m1_initial_layout.sql +++ b/src/persistence/migrations/m1_initial_layout.sql @@ -39,7 +39,7 @@ CREATE TABLE `entities` FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; -CREATE TABLE `entity_blueprints` +CREATE TABLE `entity_blueprint_links` ( `entity` INT NOT NULL, `blueprint` INT NOT NULL, @@ -50,7 +50,7 @@ CREATE TABLE `entity_blueprints` FOREIGN KEY (`blueprint`) REFERENCES `blueprints` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; -CREATE INDEX `entity_blueprints_blueprint` ON `entity_blueprints` (`blueprint`); +CREATE INDEX `entity_blueprints_blueprint` ON `entity_blueprint_links` (`blueprint`); CREATE TABLE `properties` ( diff --git a/src/values/value.cpp b/src/values/value.cpp new file mode 100644 index 0000000..52b4d18 --- /dev/null +++ b/src/values/value.cpp @@ -0,0 +1,31 @@ +#include "ikarus/values/value.h" + +#include + +#include + +#include +#include +#include +#include + +bool ikarus_value_is_indeterminate(IkarusValue const * value) { + return value->indeterminate; +} + +char const * ikarus_value_to_string(IkarusValue const * value) { + auto str = std::visit( + cppbase::overloaded{ + [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->value ? "true" : "false"; }, + [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->value); }, + [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->value); }, + }, + value->data + ); + + char * ret = new char[str.size() + 1]; + + std::strncpy(ret, str.data(), str.size() + 1); + + return ret; +} diff --git a/src/values/value.hpp b/src/values/value.hpp new file mode 100644 index 0000000..1d53154 --- /dev/null +++ b/src/values/value.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct IkarusValue { + bool indeterminate; + std::variant data; +}; From 3dd30d74c5606068b7287b96e9954c83f546d82b Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 26 Nov 2023 13:55:14 +0100 Subject: [PATCH 016/166] implement toggle/number/text values Signed-off-by: Folling --- include/ikarus/errors.h | 16 +++++----- include/ikarus/values/number_value.h | 14 ++++----- include/ikarus/values/text_value.h | 15 ++++----- include/ikarus/values/toggle_value.h | 14 ++++----- include/ikarus/values/value.h | 18 +++++++---- src/values/number_value.cpp | 26 ++++++++++++++++ src/values/number_value.hpp | 30 ++++++++++++++++-- src/values/text_value.cpp | 26 ++++++++++++++++ src/values/text_value.hpp | 29 ++++++++++++++++-- src/values/toggle_value.cpp | 26 ++++++++++++++++ src/values/toggle_value.hpp | 29 ++++++++++++++++-- src/values/value.cpp | 46 +++++++++++++++++++++++++--- src/values/value.hpp | 37 ++++++++++++++++++++-- vendor/sqlitecpp | 2 +- 14 files changed, 280 insertions(+), 48 deletions(-) create mode 100644 src/values/number_value.cpp create mode 100644 src/values/text_value.cpp create mode 100644 src/values/toggle_value.cpp diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index f501c22..ce2f31d 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -33,28 +33,28 @@ enum IkarusErrorInfo { /// \brief No error occurred. IkarusErrorInfo_Type_None = 0x0002000000000000, /// \brief The user misused the API. - /// \example Accessing a resource that does not exist. + /// Example: Accessing a resource that does not exist. IkarusErrorInfo_Type_Client_Misuse = 0x0002000100000001, /// \brief The user provided invalid input. - /// \example Passing null for a pointer that must not be null. + /// Example: Passing null for a pointer that must not be null. IkarusErrorInfo_Type_Client_Input = 0x0002000100000002, /// \brief An error occurred while interacting with a dependency from ikarus. - /// \example An error occurred in the underlying OS library. + /// Example: An error occurred in the underlying OS library. IkarusErrorInfo_Type_SubSystem_Dependency = 0x0002000200000001, /// \brief An error occurred while interacting with the database. - /// \example An error occurred while executing a query. + /// Example: An error occurred while executing a query. IkarusErrorInfo_Type_SubSystem_Database = 0x0002000200000002, /// \brief An error occurred while interacting with the filesystem. - /// \example An error occurred while reading a file. + /// Example: An error occurred while reading a file. IkarusErrorInfo_Type_SubSystem_Filesystem = 0x0002000200000003, /// \brief A datapoint within ikarus is invalid for the current state of the system. - /// \example The name of an object is found to be invalid UTF8. + /// Example: The name of an object is found to be invalid UTF8. IkarusErrorInfo_Type_LibIkarus_InvalidState = 0x0002000300000001, /// \brief LibIkarus is unable to perform a certain operation that should succeed. - /// \example Migrating a project fails + /// Example: Migrating a project fails IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation = 0x0002000300000002, /// \brief LibIkarus is unable to perform a certain operation within a given timeframe. - /// \example A query takes longer than the timeout. + /// Example: A query takes longer than the timeout. IkarusErrorInfo_Type_LibIkarus_Timeout = 0x0002000300000003, /// \brief The type of error is unknown. IkarusErrorInfo_Type_Unknown = 0xFFFFFFFF, diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index 2c054f8..a489cd6 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -24,21 +24,21 @@ IKA_API IkarusNumberValue * ikarus_number_value_create(long double value); /// \remark Must be freed with #ikarus_free. IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); +/// \brief Fetches the underlying value of a number value. +/// \param value The number value. +/// \return The underlying value. +/// \warning Undefined if the value is indeterminate. +IKA_API long double ikarus_number_value_get(IkarusNumberValue const * value); + /// \brief Sets the value of a number value. /// \param value The number value. /// \pre \li Must not be null. /// \param new_value The new value. IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); -/// \brief Fetches the underlying value of a number value. -/// \param value The number value. -/// \return The underlying value. -/// \warning If the value is indeterminate, false is returned. -IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value); - /// \brief Converts a number value to an entity value. /// \param number_value The number value to convert. /// \return The converted entity value. -IKA_API struct IkarusEntityValue * ikarus_number_value_to_entity_value(IkarusNumberValue * number_value); +IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * number_value); IKARUS_END_HEADER diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index 867a638..b9ea9c9 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -24,21 +24,22 @@ IKA_API IkarusTextValue * ikarus_text_value_create(char const * value); /// \remark Must be freed with #ikarus_free. IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); +/// \brief Fetches the underlying value of a text value. +/// \param value The text value. +/// \return The underlying value. +/// \warning Undefined if the value is indeterminate. +/// \remark The value is owned by libikarus and must not be freed. +IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); + /// \brief Sets the value of a text value. /// \param value The text value. /// \pre \li Must not be null. /// \param new_value The new value. IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); -/// \brief Fetches the underlying value of a text value. -/// \param value The text value. -/// \return The underlying value. -/// \warning If the value is indeterminate, false is returned. -IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); - /// \brief Converts a text value to an entity value. /// \param text_value The text value to convert. /// \return The converted entity value. -IKA_API struct IkarusEntityValue * ikarus_text_value_to_entity_value(IkarusTextValue * text_value); +IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * text_value); IKARUS_END_HEADER diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index 7c77870..e653482 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -24,21 +24,21 @@ IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value); /// \remark Must be freed with #ikarus_free. IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); +/// \brief Fetches the underlying value of a toggle value. +/// \param value The toggle value. +/// \return The underlying value. +/// \warning Undefined if the value is indeterminate. +IKA_API bool ikarus_toggle_value_get(IkarusToggleValue const * value); + /// \brief Sets the value of a toggle value. /// \param value The toggle value. /// \pre \li Must not be null. /// \param new_value The new value. IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); -/// \brief Fetches the underlying value of a toggle value. -/// \param value The toggle value. -/// \return The underlying value. -/// \warning If the value is indeterminate, false is returned. -IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value); - /// \brief Converts a toggle value to an entity value. /// \param toggle_value The toggle value to convert. /// \return The converted entity value. -IKA_API struct IkarusEntityValue * ikarus_toggle_value_to_entity_value(IkarusToggleValue * toggle_value); +IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * toggle_value); IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index fd17f5b..b8b95fe 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -4,7 +4,6 @@ /// \author Folling #include -#include /// \defgroup entity_value Entity Values /// \brief The values stored in entities. @@ -25,15 +24,22 @@ struct IkarusValue; /// \return True if the value is indeterminate, false otherwise. IKA_API bool ikarus_value_is_indeterminate(IkarusValue const * value); -// \brief Converts an entity value to a string. -// \pre \li Must not be null. -// \param value The entity value. -// \return A string representation of the value or null if an error occurred. -// \remark The returned value is a copy and owned by the caller. +/// \brief Sets the indeterminate state of a value. +/// \param value The value. +/// \pre \li Must not be null. +/// \param indeterminate The new indeterminate state. +IKA_API void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate); + +/// \brief Converts an entity value to a string. +/// \pre \li Must not be null. +/// \param value The entity value. +/// \return A string representation of the value or null if an error occurred. +/// \remark The returned value is owned by the caller. IKA_API char const * ikarus_value_to_string(IkarusValue const * value); /// \brief Visits an entity value, calling the appropriate function for the value's type. /// \param value The entity value to visit. +/// \pre \li Must not be null. /// \param toggle_visitor The function to call if the value is a toggle value. Skipped if null. /// \param number_visitor The function to call if the value is a number value. Skipped if null. /// \param text_visitor The function to call if the value is a text value. Skipped if null. diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp new file mode 100644 index 0000000..b4d3413 --- /dev/null +++ b/src/values/number_value.cpp @@ -0,0 +1,26 @@ +#include "ikarus/values/number_value.h" + +#include + +IkarusNumberValue * ikarus_number_value_create(long double value) { + return new IkarusNumberValue{value}; +} + +IkarusNumberValue * ikarus_number_value_create_indeterminate() { + auto * ret = new IkarusNumberValue{0.0}; + ret->set_intermediate(true); + + return ret; +} + +long double ikarus_number_value_get(IkarusNumberValue const * value) { + return value->get_value(); +} + +void ikarus_number_value_set(IkarusNumberValue * value, long double new_value) { + value->set_value(new_value); +} + +struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * number_value) { + return static_cast(number_value); +} diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index 51020fc..e495be5 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -1,5 +1,31 @@ #pragma once -struct IkarusNumberValue { - long double value; +#include + +/// \private +struct IkarusNumberValue final : IkarusValue { +public: + explicit IkarusNumberValue(long double value): + IkarusValue{this}, + _value{value} {} + + IkarusNumberValue(IkarusNumberValue const&) = default; + IkarusNumberValue(IkarusNumberValue&&) = default; + + IkarusNumberValue& operator=(IkarusNumberValue const&) = default; + IkarusNumberValue& operator=(IkarusNumberValue&&) = default; + + ~IkarusNumberValue() override = default; + +public: + [[nodiscard]] long double get_value() const { + return _value; + } + + void set_value(long double value) { + _value = value; + } + +private: + long double _value; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp new file mode 100644 index 0000000..6eb839c --- /dev/null +++ b/src/values/text_value.cpp @@ -0,0 +1,26 @@ +#include "ikarus/values/text_value.h" + +#include "text_value.hpp" + +IkarusTextValue * ikarus_text_value_create(char const * value) { + return new IkarusTextValue{value}; +} + +IkarusTextValue * ikarus_text_value_create_indeterminate() { + auto * ret = new IkarusTextValue{""}; + ret->set_intermediate(true); + + return ret; +} + +char const * ikarus_text_value_get(IkarusTextValue const * value) { + return value->get_value().data(); +} + +void ikarus_text_value_set(IkarusTextValue * value, char const * new_value) { + value->set_value(new_value); +} + +struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * text_value) { + return static_cast(text_value); +} diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 20880c4..3766513 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -2,7 +2,32 @@ #include +#include + /// \private -struct IkarusTextValue { - std::string value; +struct IkarusTextValue final : IkarusValue { +public: + explicit IkarusTextValue(std::string value): + IkarusValue{this}, + _value(std::move(value)) {} + + IkarusTextValue(IkarusTextValue const&) = default; + IkarusTextValue(IkarusTextValue&&) noexcept = default; + + IkarusTextValue& operator=(IkarusTextValue const&) = default; + IkarusTextValue& operator=(IkarusTextValue&&) noexcept = default; + + ~IkarusTextValue() override = default; + +public: + [[nodiscard]] std::string_view get_value() const noexcept { + return _value; + } + + void set_value(std::string_view value) { + _value = value; + } + +private: + std::string _value; }; diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp new file mode 100644 index 0000000..30e64c8 --- /dev/null +++ b/src/values/toggle_value.cpp @@ -0,0 +1,26 @@ +#include "ikarus/values/toggle_value.h" + +#include "toggle_value.hpp" + +IkarusToggleValue * ikarus_toggle_value_create(bool value) { + return new IkarusToggleValue{value}; +} + +IkarusToggleValue * ikarus_toggle_value_create_indeterminate() { + auto * ret = new IkarusToggleValue{false}; + ret->set_intermediate(true); + + return ret; +} + +bool ikarus_toggle_value_get(IkarusToggleValue const * value) { + return value->get_value(); +} + +void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value) { + value->set_value(new_value); +} + +struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * toggle_value) { + return static_cast(toggle_value); +} diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index a2f2d70..b526476 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -1,6 +1,31 @@ #pragma once +#include + /// \private -struct IkarusToggleValue { - bool value; +struct IkarusToggleValue final : IkarusValue { +public: + explicit IkarusToggleValue(bool value): + IkarusValue{this}, + _value{value} {} + + IkarusToggleValue(IkarusToggleValue const&) = default; + IkarusToggleValue(IkarusToggleValue&&) = default; + + IkarusToggleValue& operator=(IkarusToggleValue const&) = default; + IkarusToggleValue& operator=(IkarusToggleValue&&) = default; + + ~IkarusToggleValue() override = default; + +public: + [[nodiscard]] bool get_value() const { + return _value; + } + + void set_value(bool value) { + _value = value; + } + +private: + bool _value; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index 52b4d18..f61f2bb 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -10,22 +10,60 @@ #include bool ikarus_value_is_indeterminate(IkarusValue const * value) { - return value->indeterminate; + return value->is_interminate(); +} + +void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { + value->set_intermediate(indeterminate); } char const * ikarus_value_to_string(IkarusValue const * value) { - auto str = std::visit( + auto const str = std::visit( cppbase::overloaded{ [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->value ? "true" : "false"; }, [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->value); }, [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->value); }, }, - value->data + value->get_data() ); - char * ret = new char[str.size() + 1]; + auto * const ret = new char[str.size() + 1]; std::strncpy(ret, str.data(), str.size() + 1); return ret; } + +void ikarus_value_visit( + IkarusValue * value, + void (*toggle_visitor)(IkarusToggleValue *, void *), + void (*number_visitor)(IkarusNumberValue *, void *), + void (*text_visitor)(IkarusTextValue *, void *), + void * data +) { + std::visit( + cppbase::overloaded{ + [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); }, + }, + value->get_data() + ); +} + +void ikarus_value_visit_const( + IkarusValue const * value, + void (*toggle_visitor)(IkarusToggleValue const *, void *), + void (*number_visitor)(IkarusNumberValue const *, void *), + void (*text_visitor)(IkarusTextValue const *, void *), + void * data +) { + std::visit( + cppbase::overloaded{ + [toggle_visitor, data](IkarusToggleValue const * toggle_value) { toggle_visitor(toggle_value, data); }, + [number_visitor, data](IkarusNumberValue const * number_value) { number_visitor(number_value, data); }, + [text_visitor, data](IkarusTextValue const * text_value) { text_visitor(text_value, data); }, + }, + value->get_data() + ); +} diff --git a/src/values/value.hpp b/src/values/value.hpp index 1d53154..0adc61c 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -3,6 +3,39 @@ #include struct IkarusValue { - bool indeterminate; - std::variant data; +public: + using Data = std::variant::variant; + +public: + explicit IkarusValue(Data data): + _data(data) {} + + IkarusValue(IkarusValue const&) = default; + IkarusValue(IkarusValue&&) noexcept = default; + + IkarusValue& operator=(IkarusValue const&) = default; + IkarusValue& operator=(IkarusValue&&) noexcept = default; + + virtual ~IkarusValue(); + +public: + [[nodiscard]] inline bool is_interminate() const { + return _indeterminate; + } + + void set_intermediate(bool value) { + _indeterminate = value; + } + + [[nodiscard]] inline Data& get_data() { + return _data; + } + + [[nodiscard]] inline Data const& get_data() const { + return _data; + } + +private: + bool _indeterminate{false}; + Data _data; }; diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 538616d..3057656 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 538616d8ced2d3f04659261ccae3b039d65da004 +Subproject commit 3057656ff277294ab424af90e553e630c2a5e8f7 From 7a7f7462a4a93fd0f9adbe6944bb6802e23e4c18 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 27 Nov 2023 11:24:55 +0100 Subject: [PATCH 017/166] update sqlitecpp & merge property settings into properties Signed-off-by: Folling --- include/ikarus/errors.h | 2 +- include/ikarus/global.h | 2 +- src/id.hpp => include/ikarus/id.h | 0 include/ikarus/objects/object.h | 21 ++-- .../objects/properties/number_property.h | 25 +++- include/ikarus/objects/properties/property.h | 20 ++- .../ikarus/objects/properties/property_type.h | 6 - .../ikarus/objects/properties/text_property.h | 25 +++- .../objects/properties/toggle_property.h | 25 +++- include/ikarus/values/number_value.h | 4 +- include/ikarus/values/text_value.h | 4 +- include/ikarus/values/toggle_value.h | 4 +- include/ikarus/values/value.h | 2 +- src/errors.cpp | 30 ++--- src/id.cpp | 24 +--- src/objects/blueprint.cpp | 66 +++++----- src/objects/blueprint.hpp | 14 ++- src/objects/entity.cpp | 0 src/objects/entity.hpp | 11 +- src/objects/object.cpp | 13 ++ src/objects/object.hpp | 29 +++-- src/objects/properties/number_property.cpp | 0 src/objects/properties/number_property.hpp | 5 + src/objects/properties/property.cpp | 0 src/objects/properties/property.hpp | 15 +++ src/objects/properties/property_source.cpp | 0 src/objects/properties/property_source.hpp | 30 +++++ src/objects/properties/text_property.cpp | 0 src/objects/properties/text_property.hpp | 8 ++ src/objects/properties/toggle_property.cpp | 0 src/objects/properties/toggle_property.hpp | 7 ++ src/objects/property.cpp | 13 -- src/objects/property.hpp | 9 -- src/objects/property_source.hpp | 10 -- src/persistence/function_context.cpp | 18 +++ src/persistence/function_context.hpp | 53 ++++++++ src/persistence/project.cpp | 49 ++++++++ src/persistence/project.hpp | 119 ++++-------------- vendor/sqlitecpp | 2 +- 39 files changed, 412 insertions(+), 253 deletions(-) rename src/id.hpp => include/ikarus/id.h (100%) create mode 100644 src/objects/entity.cpp create mode 100644 src/objects/object.cpp create mode 100644 src/objects/properties/number_property.cpp create mode 100644 src/objects/properties/number_property.hpp create mode 100644 src/objects/properties/property.cpp create mode 100644 src/objects/properties/property.hpp create mode 100644 src/objects/properties/property_source.cpp create mode 100644 src/objects/properties/property_source.hpp create mode 100644 src/objects/properties/text_property.cpp create mode 100644 src/objects/properties/text_property.hpp create mode 100644 src/objects/properties/toggle_property.cpp create mode 100644 src/objects/properties/toggle_property.hpp delete mode 100644 src/objects/property.cpp delete mode 100644 src/objects/property.hpp delete mode 100644 src/objects/property_source.hpp create mode 100644 src/persistence/function_context.cpp create mode 100644 src/persistence/function_context.hpp create mode 100644 src/persistence/project.cpp diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index ce2f31d..9dc876d 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -57,7 +57,7 @@ enum IkarusErrorInfo { /// Example: A query takes longer than the timeout. IkarusErrorInfo_Type_LibIkarus_Timeout = 0x0002000300000003, /// \brief The type of error is unknown. - IkarusErrorInfo_Type_Unknown = 0xFFFFFFFF, + IkarusErrorInfo_Type_Unknown = 0xFFFFFFFFFFFFFFFF, }; /// \brief Gets the name of an error info. diff --git a/include/ikarus/global.h b/include/ikarus/global.h index 1572b24..bf9e5d5 100644 --- a/include/ikarus/global.h +++ b/include/ikarus/global.h @@ -5,7 +5,7 @@ #include -/// \addtogroup global Global +/// \defgroup global Global /// \brief Information relevant to the entire library. /// @{ diff --git a/src/id.hpp b/include/ikarus/id.h similarity index 100% rename from src/id.hpp rename to include/ikarus/id.h diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index a2cce6a..abaa580 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -8,12 +8,9 @@ /// \defgroup object Objects /// \brief Objects are a compound type of all types of objects in the database. /// \details The following objects currently exist: -/// - blueprints -/// - properties -/// - entities -/// - blueprint folders -/// - property folders -/// - entity folders +/// - \ref blueprint.h "Blueprints" +/// - \ref property.h "Properties" +/// - \ref entity.h "Entities" /// @{ IKARUS_BEGIN_HEADER @@ -21,6 +18,16 @@ IKARUS_BEGIN_HEADER /// \brief A generic object. Wraps all types of objects, including folders. struct IkarusObject; +/// \brief Compares two objects for equality. +/// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the same +/// object. +/// \param lhs The left hand side object. +/// \pre \li Must not be null. +/// \param rhs The right hand side object. +/// \pre \li Must not be null. +/// \return True if the objects are equal, false otherwise. +IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs); + /// \brief Visits an object. Calling the appropriate function for the object's type. /// \param object The object to visit. /// \param blueprint_visitor The function to call if the object is a blueprint. Skipped if null. @@ -55,4 +62,4 @@ IKA_API void ikarus_object_visit_const( IKARUS_END_HEADER -// @} +/// @} diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 9045140..7787cc6 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -14,10 +14,27 @@ IKARUS_BEGIN_HEADER struct IkarusNumberProperty; IKA_API IkarusNumberProperty * ikarus_number_property_create( - struct IkarusProject * project, - char const * name, - struct IkarusPropertySource * property_source, - struct IkarusNumberSettings * settings + struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source +); + +/// \brief Sets the default value for a number property. +/// \param property The number property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(struct IkarusNumberProperty * property); + +/// \brief Sets the default value for a number property. +/// \param property The number property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values +/// and other settings. +IKA_API void ikarus_number_property_set_default_value( + struct IkarusNumberProperty * property, struct IkarusNumberValue * default_value ); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index f1ebf49..1da35e6 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -29,8 +29,12 @@ IKARUS_BEGIN_HEADER /// /// Every property has settings which can be used to customise the property further. /// Two settings that are shared among all properties are the following: -/// - Multiple -/// - Allow undefined +/// - List +/// - May be undefined +/// +/// Additionally, each property has a default value. If no default value is provided, a sensible default is chosen. +/// Setting a default value that isn't valid for the property is an error. Changing settings so that the current default value +/// becomes invalid is valid but unsets the custom default value. /// /// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. /// The latter allows you to specify an "unknown" value for a property. @@ -43,6 +47,7 @@ IKARUS_BEGIN_HEADER /// /// We call properties within entities "Entity Properties" and properties within blueprints "Blueprint Properties". /// +/// /// \remark Properties are scoped to the blueprint or entity they are associated with. /// \remark Values for properties are lazily created as space saving measure. /// Fetching the value for some property of some entity will return the property's default value if none is specified. @@ -65,17 +70,6 @@ IKA_API void ikarus_property_delete(IkarusProperty * property); /// \remark Must be freed using #ikarus_free. IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property); -/// \brief Gets the settings of a property. -/// \param property The property to get the settings of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The settings of the property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySettings * ikarus_property_get_settings(IkarusProperty * property); - -/// \see ikarus_property_get_settings -IKA_API struct IkarusPropertySettings const * ikarus_property_get_settings_const(IkarusProperty const * property); - /// \brief Gets the source of a property. /// \param property The property to get the source of. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/properties/property_type.h b/include/ikarus/objects/properties/property_type.h index 89e5fa1..ee6ec19 100644 --- a/include/ikarus/objects/properties/property_type.h +++ b/include/ikarus/objects/properties/property_type.h @@ -22,12 +22,6 @@ enum IkarusPropertyType { IkarusPropertyType_Text, }; -/// \brief Fetches the default value for a property type. -/// \remark Not to be confused with the default value of a property. See ikarus_property_get_default_value -/// \param type The property type. -/// \return The default value for the property type or null if an error occurs. -IKA_API struct IkarusValue * ikarus_property_type_get_default_default_value(IkarusPropertyType type); - IKARUS_END_HEADER /// @} diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index f7975b3..ad90bf6 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -14,10 +14,27 @@ IKARUS_BEGIN_HEADER struct IkarusTextProperty; IKA_API IkarusTextProperty * ikarus_text_property_create( - struct IkarusProject * project, - char const * name, - struct IkarusPropertySource * property_source, - struct IkarusTextSettings * settings + struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source +); + +/// \brief Sets the default value for a text property. +/// \param property The text property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property); + +/// \brief Sets the default value for a text property. +/// \param property The text property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values +/// and other settings. +IKA_API void ikarus_text_property_set_default_value( + struct IkarusTextProperty * property, struct IkarusTextValue * default_value ); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index baa0801..9e8119d 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -14,10 +14,27 @@ IKARUS_BEGIN_HEADER struct IkarusToggleProperty; IKA_API IkarusToggleProperty * ikarus_toggle_property_create( - struct IkarusProject * project, - char const * name, - struct IkarusPropertySource * property_source, - struct IkarusToggleSettings * settings + struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source +); + +/// \brief Sets the default value for a toggle property. +/// \param property The toggle property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property); + +/// \brief Sets the default value for a toggle property. +/// \param property The toggle property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values +/// and other settings. +IKA_API void ikarus_toggle_property_set_default_value( + struct IkarusToggleProperty * property, struct IkarusToggleValue * default_value ); IKARUS_END_HEADER diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index a489cd6..d4b6446 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -5,7 +5,7 @@ #include -/// \addtogroup entity_value Entity Values +/// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER @@ -42,3 +42,5 @@ IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * number_value); IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index b9ea9c9..8562ab5 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -5,7 +5,7 @@ #include -/// \addtogroup entity_value Entity Values +/// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER @@ -43,3 +43,5 @@ IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * text_value); IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index e653482..fed97d2 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -5,7 +5,7 @@ #include -/// \addtogroup entity_value Entity Values +/// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER @@ -42,3 +42,5 @@ IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * toggle_value); IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index b8b95fe..1cf1fd0 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -5,7 +5,7 @@ #include -/// \defgroup entity_value Entity Values +/// \defgroup values Values /// \brief The values stored in entities. /// \details Each entity has a value for each property it is associated with. /// The value is of the type specified by the property and constrained by the property's settings. diff --git a/src/errors.cpp b/src/errors.cpp index 19c3bf9..54681f2 100644 --- a/src/errors.cpp +++ b/src/errors.cpp @@ -2,21 +2,21 @@ char const * get_error_info_name(IkarusErrorInfo info) { switch (info) { - case IkarusErrorInfo_Source_None: return "IkarusErrorSource_None"; - case IkarusErrorInfo_Source_Client: return "IkarusErrorSource_Client"; - case IkarusErrorInfo_Source_SubSystem: return "IkarusErrorSource_SubSystem"; - case IkarusErrorInfo_Source_LibIkarus: return "IkarusErrorSource_LibIkarus"; - case IkarusErrorInfo_Source_Unknown: return "IkarusErrorSource_Unknown"; - case IkarusErrorInfo_Type_None: return "IkarusErrorType_None"; - case IkarusErrorInfo_Type_Client_Misuse: return "IkarusErrorType_Client_Misuse"; - case IkarusErrorInfo_Type_Client_Input: return "IkarusErrorType_Client_Input"; - case IkarusErrorInfo_Type_SubSystem_Dependency: return "IkarusErrorType_SubSystem_Dependency"; - case IkarusErrorInfo_Type_SubSystem_Database: return "IkarusErrorType_SubSystem_Database"; - case IkarusErrorInfo_Type_SubSystem_Filesystem: return "IkarusErrorType_SubSystem_Filesystem"; - case IkarusErrorInfo_Type_LibIkarus_InvalidState: return "IkarusErrorType_LibIkarus_InvalidState"; - case IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation: return "IkarusErrorType_LibIkarus_CannotPerformOperation"; - case IkarusErrorInfo_Type_LibIkarus_Timeout: return "IkarusErrorType_LibIkarus_Timeout"; - case IkarusErrorInfo_Type_Unknown: return "IkarusErrorType_Unknown"; + case IkarusErrorInfo_Source_None: return "IkarusErrorInfo_Source_None"; + case IkarusErrorInfo_Source_Client: return "IkarusErrorInfo_Source_Client"; + case IkarusErrorInfo_Source_SubSystem: return "IkarusErrorInfo_Source_SubSystem"; + case IkarusErrorInfo_Source_LibIkarus: return "IkarusErrorInfo_Source_LibIkarus"; + case IkarusErrorInfo_Source_Unknown: return "IkarusErrorInfo_Source_Unknown"; + case IkarusErrorInfo_Type_None: return "IkarusErrorInfo_Type_None"; + case IkarusErrorInfo_Type_Client_Misuse: return "IkarusErrorInfo_Type_Client_Misuse"; + case IkarusErrorInfo_Type_Client_Input: return "IkarusErrorInfo_Type_Client_Input"; + case IkarusErrorInfo_Type_SubSystem_Dependency: return "IkarusErrorInfo_Type_SubSystem_Dependency"; + case IkarusErrorInfo_Type_SubSystem_Database: return "IkarusErrorInfo_Type_SubSystem_Database"; + case IkarusErrorInfo_Type_SubSystem_Filesystem: return "IkarusErrorInfo_Type_SubSystem_Filesystem"; + case IkarusErrorInfo_Type_LibIkarus_InvalidState: return "IkarusErrorInfo_Type_LibIkarus_InvalidState"; + case IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation: return "IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation"; + case IkarusErrorInfo_Type_LibIkarus_Timeout: return "IkarusErrorInfo_Type_LibIkarus_Timeout"; + case IkarusErrorInfo_Type_Unknown: return "IkarusErrorInfo_Type_Unknown"; default: return "Unknown"; } } diff --git a/src/id.cpp b/src/id.cpp index 7ec6491..463aa3d 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -1,32 +1,16 @@ -#include "id.hpp" +#include "ikarus/id.h" #include #include -uint64_t const IKARUS_ID_OBJECT_TYPE_BITS = 8; -uint64_t const IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; +constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; +constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; -auto from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { +auto ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); } auto ikarus_id_get_object_type(IkarusId id) -> IkarusObjectType { return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); } - -TEST_CASE("id_object_type", "[id]") { - // NOLINTNEXTLINE(readability-magic-numbers) - auto id = static_cast(IkarusObjectType_Blueprint) << IKARUS_ID_OBJECT_RANDOM_BITS; - - REQUIRE(ikarus_id_get_object_type(id) == IkarusObjectType_Blueprint); -} - -TEST_CASE("id_equal", "[id]") { - auto id = static_cast(IkarusObjectType_Blueprint) << IKARUS_ID_OBJECT_RANDOM_BITS; - auto copy = id; - auto third = static_cast(IkarusObjectType_Property) << IKARUS_ID_OBJECT_RANDOM_BITS; - - REQUIRE(ikarus_id_is_equal(id, copy)); - REQUIRE(!ikarus_id_is_equal(id, third)); -} diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index d6a38b1..3971d9e 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -8,9 +8,12 @@ #include #include -#include +#include #include +IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): + IkarusObject{project, id} {} + IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { LOG_INFO("creating new blueprint"); @@ -19,7 +22,7 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - auto * ctx = project->function_context(); + auto * ctx = project->get_function_context(); if (name == nullptr) { ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); @@ -31,12 +34,12 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - LOG_DEBUG("project={}; name={}", project->path().c_str(), name); + LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); VTRYRV( - auto id, + auto const id, nullptr, - project->db() + project->get_db() ->transact([name](auto * db) -> cppbase::Result { LOG_VERBOSE("creating blueprint in objects table"); @@ -79,14 +82,15 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { return; } - auto * ctx = blueprint->project->function_context(); + auto * ctx = blueprint->get_project()->get_function_context(); - LOG_DEBUG("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->get_id()); TRYRV( , - blueprint->project->db() - ->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id) + blueprint->get_project() + ->get_db() + ->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->get_id()) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to delete blueprint from objects table: {}", err), @@ -99,7 +103,7 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { LOG_VERBOSE("blueprint was successfully deleted from database, freeing blueprint"); - blueprint->project->remove_blueprint(blueprint); + blueprint->get_project()->uncache_blueprint(blueprint); LOG_VERBOSE("successfully deleted blueprint"); } @@ -112,15 +116,16 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { return 0; } - auto * ctx = blueprint->project->function_context(); + auto * ctx = blueprint->get_project()->get_function_context(); - LOG_DEBUG("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->get_id()); VTRYRV( auto count, 0, - blueprint->project->db() - ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->id) + blueprint->get_project() + ->get_db() + ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->get_id()) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch blueprint property count: {}", err), @@ -148,25 +153,26 @@ void ikarus_blueprint_get_properties( return; } - auto * ctx = blueprint->project->function_context(); + auto * ctx = blueprint->get_project()->get_function_context(); if (properties_out == nullptr) { ctx->set_error("properties_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return; } - LOG_DEBUG("blueprint={}; properties_out_size={}", blueprint->id, properties_out_size); + LOG_DEBUG("blueprint={}; properties_out_size={}", blueprint->get_id(), properties_out_size); IkarusId ids[properties_out_size]; TRYRV( , - blueprint->project->db() + blueprint->get_project() + ->get_db() ->query_many_buffered( "SELECT `id` FROM `properties` WHERE `source` = ?", static_cast(ids), properties_out_size, - blueprint->id + blueprint->get_id() ) .on_error([ctx](auto const& err) { ctx->set_error( @@ -182,7 +188,7 @@ void ikarus_blueprint_get_properties( for (size_t i = 0; i < properties_out_size; ++i) { /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - properties_out[i] = blueprint->project->get_property(ids[i]); + properties_out[i] = blueprint->get_project()->get_property(ids[i]); } LOG_VERBOSE("successfully fetched blueprint properties"); @@ -196,15 +202,16 @@ size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprin return 0; } - auto * ctx = blueprint->project->function_context(); + auto * ctx = blueprint->get_project()->get_function_context(); - LOG_DEBUG("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->get_id()); VTRYRV( auto count, 0, - blueprint->project->db() - ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->id) + blueprint->get_project() + ->get_db() + ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->get_id()) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch blueprint linked entity count: {}", err), @@ -232,25 +239,26 @@ void ikarus_blueprint_get_linked_entities( return; } - auto * ctx = blueprint->project->function_context(); + auto * ctx = blueprint->get_project()->get_function_context(); if (entities_out == nullptr) { ctx->set_error("entities_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return; } - LOG_DEBUG("blueprint={}; entities_out_size={}", blueprint->id, entities_out_size); + LOG_DEBUG("blueprint={}; entities_out_size={}", blueprint->get_id(), entities_out_size); IkarusId ids[entities_out_size]; TRYRV( , - blueprint->project->db() + blueprint->get_project() + ->get_db() ->query_many_buffered( "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", static_cast(ids), entities_out_size, - blueprint->id + blueprint->get_id() ) .on_error([ctx](auto const& err) { ctx->set_error( @@ -266,7 +274,7 @@ void ikarus_blueprint_get_linked_entities( for (size_t i = 0; i < entities_out_size; ++i) { /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - entities_out[i] = blueprint->project->get_entity(ids[i]); + entities_out[i] = blueprint->get_project()->get_entity(ids[i]); } LOG_VERBOSE("successfully fetched blueprint linked entities"); @@ -286,7 +294,7 @@ IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * bl // auto * ctx = blueprint->project->function_context(); - LOG_DEBUG("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->get_id()); LOG_VERBOSE("successfully casted blueprint to object"); diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index f904074..445efdb 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -2,8 +2,14 @@ #include -/// \private -struct IkarusBlueprint : public IkarusObject { - inline IkarusBlueprint(struct IkarusProject * project, IkarusId id): - IkarusObject{project, id} {} +struct IkarusBlueprint final : IkarusObject { + inline IkarusBlueprint(struct IkarusProject * project, IkarusId id); + + IkarusBlueprint(IkarusBlueprint const&) = default; + IkarusBlueprint(IkarusBlueprint&&) = default; + + IkarusBlueprint& operator=(IkarusBlueprint const&) = default; + IkarusBlueprint& operator=(IkarusBlueprint&&) = default; + + ~IkarusBlueprint() override = default; }; diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index 28fb068..fb78e39 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -2,8 +2,15 @@ #include -/// \private -struct IkarusEntity : public IkarusObject { +struct IkarusEntity final : IkarusObject { inline IkarusEntity(struct IkarusProject * project, IkarusId id): IkarusObject{project, id} {} + + IkarusEntity(IkarusEntity const&) = default; + IkarusEntity(IkarusEntity&&) = default; + + IkarusEntity& operator=(IkarusEntity const&) = default; + IkarusEntity& operator=(IkarusEntity&&) = default; + + ~IkarusEntity() override = default; }; diff --git a/src/objects/object.cpp b/src/objects/object.cpp new file mode 100644 index 0000000..72aba6e --- /dev/null +++ b/src/objects/object.cpp @@ -0,0 +1,13 @@ +#include "object.hpp" + +IkarusProject * IkarusObject::get_project() { + return _project; +} + +IkarusProject * IkarusObject::get_project() const { + return _project; +} + +IkarusId IkarusObject::get_id() const { + return _id; +} diff --git a/src/objects/object.hpp b/src/objects/object.hpp index e95c3e0..ee1b147 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -1,14 +1,27 @@ #pragma once -#include - -#include +#include struct IkarusObject { - struct IkarusProject * project; - IkarusId id; +public: + IkarusObject(struct IkarusProject * project, IkarusId id); - inline IkarusObject(struct IkarusProject * project, IkarusId id): - project{project}, - id{id} {} + IkarusObject(IkarusObject const&) = default; + IkarusObject(IkarusObject&&) = default; + + IkarusObject& operator=(IkarusObject const&) = default; + IkarusObject& operator=(IkarusObject&&) = default; + + virtual ~IkarusObject() = default; + +public: + [[nodiscard]] inline struct IkarusProject * get_project(); + + [[nodiscard]] inline struct IkarusProject * get_project() const; + + [[nodiscard]] inline IkarusId get_id() const; + +private: + struct IkarusProject mutable * _project; + IkarusId _id; }; diff --git a/src/objects/properties/number_property.cpp b/src/objects/properties/number_property.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/properties/number_property.hpp b/src/objects/properties/number_property.hpp new file mode 100644 index 0000000..dd5f8a2 --- /dev/null +++ b/src/objects/properties/number_property.hpp @@ -0,0 +1,5 @@ +// +// Created by Jonathan Purol on 26.11.23. +// + +export module number_property.hpp; diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/properties/property.hpp b/src/objects/properties/property.hpp new file mode 100644 index 0000000..1a74577 --- /dev/null +++ b/src/objects/properties/property.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +struct IkarusProperty : IkarusObject { + IkarusProperty(struct IkarusProject * project, IkarusId id); + + IkarusProperty(IkarusProperty const&) = default; + IkarusProperty(IkarusProperty&&) = default; + + IkarusProperty& operator=(IkarusProperty const&) = default; + IkarusProperty& operator=(IkarusProperty&&) = default; + + ~IkarusProperty() override = default; +}; diff --git a/src/objects/properties/property_source.cpp b/src/objects/properties/property_source.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/properties/property_source.hpp b/src/objects/properties/property_source.hpp new file mode 100644 index 0000000..8da517a --- /dev/null +++ b/src/objects/properties/property_source.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include + +struct IkarusPropertySource { +public: + using Data = std::variant; + +public: + inline explicit IkarusPropertySource(Data data): + _data{data} {} + + IkarusPropertySource(IkarusPropertySource const&) = default; + IkarusPropertySource(IkarusPropertySource&&) = default; + + IkarusPropertySource& operator=(IkarusPropertySource const&) = default; + IkarusPropertySource& operator=(IkarusPropertySource&&) = default; + + ~IkarusPropertySource() = default; + +public: + [[nodiscard]] inline Data const& get_data() const { + return _data; + } + +private: + std::variant _data; +}; diff --git a/src/objects/properties/text_property.cpp b/src/objects/properties/text_property.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/properties/text_property.hpp b/src/objects/properties/text_property.hpp new file mode 100644 index 0000000..3816f35 --- /dev/null +++ b/src/objects/properties/text_property.hpp @@ -0,0 +1,8 @@ +// +// Created by Jonathan Purol on 26.11.23. +// + +#ifndef TEXT_PROPERTY_HPP +#define TEXT_PROPERTY_HPP + +#endif //TEXT_PROPERTY_HPP diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/properties/toggle_property.hpp b/src/objects/properties/toggle_property.hpp new file mode 100644 index 0000000..5514d8b --- /dev/null +++ b/src/objects/properties/toggle_property.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include + +struct IkarusToggleProperty final : IkarusProperty { + IkarusToggleProperty(struct IkarusProject * project, IkarusId id); +}; diff --git a/src/objects/property.cpp b/src/objects/property.cpp deleted file mode 100644 index 80d9031..0000000 --- a/src/objects/property.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "property.hpp" - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include diff --git a/src/objects/property.hpp b/src/objects/property.hpp deleted file mode 100644 index b59d7dd..0000000 --- a/src/objects/property.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -/// \private -struct IkarusProperty : public IkarusObject { - inline IkarusProperty(struct IkarusProject * project, IkarusId id): - IkarusObject{project, id} {} -}; diff --git a/src/objects/property_source.hpp b/src/objects/property_source.hpp deleted file mode 100644 index e2a2739..0000000 --- a/src/objects/property_source.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -#include - -/// \private -struct IkarusPropertySource { - std::variant data; -}; diff --git a/src/persistence/function_context.cpp b/src/persistence/function_context.cpp new file mode 100644 index 0000000..6e252dc --- /dev/null +++ b/src/persistence/function_context.cpp @@ -0,0 +1,18 @@ +#include "function_context.hpp" + +FunctionContext::FunctionContext(IkarusProject * project): + _project{project} {} + +FunctionContext::~FunctionContext() { + if (_project->_function_contexts.size() == 1) { + if (_project->error_message_buffer.empty()) { + _project->error_message_buffer.push_back('\0'); + } else { + _project->error_message_buffer[0] = '\0'; + } + + _project->error_infos = {}; + } + + _project->_function_contexts.pop(); +} diff --git a/src/persistence/function_context.hpp b/src/persistence/function_context.hpp new file mode 100644 index 0000000..3dc64b3 --- /dev/null +++ b/src/persistence/function_context.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +#include + +#include + +struct FunctionContext { +public: + explicit FunctionContext(struct IkarusProject * project); + + FunctionContext(FunctionContext const&) noexcept = default; + FunctionContext(FunctionContext&&) noexcept = default; + + auto operator=(FunctionContext const&) noexcept -> FunctionContext& = default; + auto operator=(FunctionContext&&) noexcept -> FunctionContext& = default; + + ~FunctionContext(); + +public: + template + requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) + auto set_error(std::string_view error_message, bool log_error, Infos... infos) -> void { + if (error_message.size() > _project->error_message_buffer.size()) { + _project->error_message_buffer.resize(error_message.size() + 1); + } + + for (int i = 0; i < error_message.size(); ++i) { + _project->error_message_buffer[i] = error_message[i]; + } + + _project->error_message_buffer[error_message.size()] = '\0'; + _project->error_infos = {infos...}; + + if (log_error) { + LOG_ERROR( + "Error({}): {}", + fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), + error_message + ); + } + } + +private: + struct IkarusProject * _project; +}; diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp new file mode 100644 index 0000000..3ceb821 --- /dev/null +++ b/src/persistence/project.cpp @@ -0,0 +1,49 @@ +#include "project.hpp" + +#include "ikarus/persistence/project.h" + +#include + +auto IkarusProject::get_name() const -> std::string_view { + return _name; +} + +auto IkarusProject::get_path() const -> std::filesystem::path const& { + return _path; +} + +auto IkarusProject::get_db() -> sqlitecpp::Connection * { + return _db.get(); +} + +auto IkarusProject::get_db() const -> sqlitecpp::Connection const * { + return _db.get(); +} + +auto IkarusProject::get_function_context() -> FunctionContext * { + return &_function_contexts.emplace(this); +} + +IkarusBlueprint * IkarusProject::get_blueprint(IkarusId id) { + return get_cached_object(id, this->_blueprints); +} + +auto IkarusProject::uncache_blueprint(IkarusBlueprint * blueprint) -> void { + remove_cached_object(blueprint, _blueprints); +} + +auto IkarusProject::get_entity(IkarusId id) -> IkarusEntity * { + return get_cached_object(id, this->_entities); +} + +auto IkarusProject::uncache_entity(IkarusEntity * entity) -> void { + remove_cached_object(entity, _entities); +} + +auto IkarusProject::get_property(IkarusId id) -> IkarusProperty * { + return get_cached_object(id, this->_properties); +} + +auto IkarusProject::uncache_property(IkarusProperty * property) -> void { + remove_cached_object(property, _properties); +} diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index fbb84f2..ef69a81 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -9,83 +8,45 @@ #include #include +#include -constexpr inline size_t MAXIMUM_ERROR_INFOS = 8; -constexpr inline size_t MAXIMUM_ERROR_MESSAGE_LENGTH = 256; - -/// \private -class FunctionContext { -public: - explicit FunctionContext(struct IkarusProject * project); - FunctionContext(FunctionContext const&) noexcept = default; - FunctionContext(FunctionContext&&) noexcept = default; - - auto operator=(FunctionContext const&) noexcept -> FunctionContext& = default; - auto operator=(FunctionContext&&) noexcept -> FunctionContext& = default; - - ~FunctionContext(); - - template - requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) - auto set_error(std::string_view error_message, bool log_error, Infos... infos) -> void; - -private: - struct IkarusProject * _project; -}; +constexpr inline auto MAXIMUM_ERROR_INFOS = 8; /// \private struct IkarusProject { public: - [[nodiscard]] inline auto name() const -> std::string_view { - return _name; - } + [[nodiscard]] auto get_name() const -> std::string_view; - [[nodiscard]] inline auto path() const -> std::filesystem::path const& { - return _path; - } + [[nodiscard]] auto get_path() const -> std::filesystem::path const&; - [[nodiscard]] inline auto db() -> sqlitecpp::Connection * { - return _db.get(); - } + [[nodiscard]] auto get_db() -> sqlitecpp::Connection *; + [[nodiscard]] auto get_db() const -> sqlitecpp::Connection const *; - inline auto function_context() -> FunctionContext * { - return &_function_contexts.emplace(this); - } +public: + [[nodiscard]] auto get_function_context() -> struct FunctionContext *; - [[nodiscard]] IkarusBlueprint * get_blueprint(IkarusId id) { - return get_cached_object(id, this->_blueprints); - } +public: + [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; + auto uncache_blueprint(struct IkarusBlueprint * blueprint) -> void; - auto remove_blueprint(IkarusBlueprint * blueprint) -> void { - remove_cached_object(blueprint, _blueprints); - } + [[nodiscard]] auto get_entity(IkarusId id) -> struct IkarusEntity *; + auto uncache_entity(struct IkarusEntity * entity) -> void; - [[nodiscard]] auto get_entity(IkarusId id) -> IkarusEntity * { - return get_cached_object(id, this->_entities); - } - - auto remove_entity(IkarusEntity * entity) -> void { - remove_cached_object(entity, _entities); - } - - [[nodiscard]] auto get_property(IkarusId id) -> IkarusProperty * { - return get_cached_object(id, this->_properties); - } - - auto remove_property(IkarusProperty * property) -> void { - remove_cached_object(property, _properties); - } + [[nodiscard]] auto get_property(IkarusId id) -> struct IkarusProperty *; + auto uncache_property(struct IkarusProperty * property) -> void; private: template [[nodiscard]] T * get_cached_object(IkarusId id, std::unordered_map>& cache) { - if (auto iter = cache.find(id); iter == cache.cend()) { + auto const iter = cache.find(id); + + if (iter == cache.cend()) { auto [ret_iter, _] = cache.emplace(id, std::make_unique(this, id)); return ret_iter->second.get(); - } else { - return iter->second.get(); } + + return iter->second.get(); } template @@ -94,7 +55,7 @@ private: } private: - friend class FunctionContext; + friend struct FunctionContext; std::string _name; std::filesystem::path _path; @@ -109,41 +70,3 @@ private: std::stack _function_contexts; }; - -FunctionContext::FunctionContext(struct IkarusProject * project): - _project{project} {} - -FunctionContext::~FunctionContext() { - if (_project->_function_contexts.size() == 1) { - if (_project->error_message_buffer.empty()) { - _project->error_message_buffer.push_back('\0'); - } else { - _project->error_message_buffer[0] = '\0'; - } - - _project->error_infos = {}; - } - - _project->_function_contexts.pop(); -} - -template - requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) -auto FunctionContext::set_error(std::string_view error_message, bool log_error, Infos... infos) -> void { - if (error_message.size() > _project->error_message_buffer.size()) { - _project->error_message_buffer.resize(error_message.size() + 1); - } - - for (int i = 0; i < error_message.size(); ++i) { - _project->error_message_buffer[i] = error_message[i]; - } - - _project->error_message_buffer[error_message.size()] = '\0'; - _project->error_infos = {infos...}; - - if (log_error) { - LOG_ERROR( - "Error({}): {}", fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), error_message - ); - } -} diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 3057656..538616d 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 3057656ff277294ab424af90e553e630c2a5e8f7 +Subproject commit 538616d8ced2d3f04659261ccae3b039d65da004 From 549bad7aac1a7a21340a04bad737615e7b994062 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 27 Nov 2023 13:13:35 +0100 Subject: [PATCH 018/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 538616d..07b806f 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 538616d8ced2d3f04659261ccae3b039d65da004 +Subproject commit 07b806f4d73b27a555a3ba82a97d89913e430e48 From cc73cf000ddd251726cc99f9961c077c8d1aa64f Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 27 Nov 2023 13:13:54 +0100 Subject: [PATCH 019/166] fixup compile errors and allow fetching property properly from cache Signed-off-by: Folling --- include/ikarus/objects/object_type.h | 8 +- .../objects/properties/number_property.h | 1 - .../ikarus/objects/properties/text_property.h | 1 - .../objects/properties/toggle_property.h | 1 - src/objects/blueprint.cpp | 99 +++++++------------ src/objects/blueprint.hpp | 2 +- src/objects/object.cpp | 4 + src/objects/object.hpp | 6 +- src/objects/properties/number_property.cpp | 4 + src/objects/properties/number_property.hpp | 11 ++- src/objects/properties/property.cpp | 57 +++++++++++ src/objects/properties/property.hpp | 23 ++++- src/objects/properties/text_property.cpp | 4 + src/objects/properties/text_property.hpp | 12 +-- src/objects/properties/toggle_property.cpp | 6 ++ src/objects/properties/toggle_property.hpp | 1 + src/persistence/function_context.cpp | 2 +- src/persistence/project.cpp | 26 ++++- src/persistence/project.hpp | 17 ++-- src/values/value.cpp | 25 ++++- src/values/value.hpp | 25 ++--- 21 files changed, 213 insertions(+), 122 deletions(-) diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 578919a..efb6609 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -15,11 +15,11 @@ enum IkarusObjectType { /// \brief Not an object or no object. IkarusObjectType_None = 0, /// \brief An IkarusEntity. - IkarusObjectType_Entity = 0b00000011, - /// \brief An IkarusProperty. - IkarusObjectType_Property = 0b00000010, + IkarusObjectType_Entity = 0b00000001, /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 0b00000001, + IkarusObjectType_Blueprint = 0b00000010, + /// \brief An IkarusProperty. + IkarusObjectType_Property = 0b00000011, }; IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 7787cc6..9b4fa4d 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -21,7 +21,6 @@ IKA_API IkarusNumberProperty * ikarus_number_property_create( /// \param property The number property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \remark The settings take ownership of the value, the caller must not free it. IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(struct IkarusNumberProperty * property); /// \brief Sets the default value for a number property. diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index ad90bf6..0e6a435 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -21,7 +21,6 @@ IKA_API IkarusTextProperty * ikarus_text_property_create( /// \param property The text property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \remark The settings take ownership of the value, the caller must not free it. IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property); /// \brief Sets the default value for a text property. diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index 9e8119d..0d67304 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -21,7 +21,6 @@ IKA_API IkarusToggleProperty * ikarus_toggle_property_create( /// \param property The toggle property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \remark The settings take ownership of the value, the caller must not free it. IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property); /// \brief Sets the default value for a toggle property. diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index 3971d9e..bdbea93 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -17,25 +17,15 @@ IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { LOG_INFO("creating new blueprint"); - if (project == nullptr) { - LOG_ERROR("project is nullptr"); - return nullptr; - } + LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); auto * ctx = project->get_function_context(); - if (name == nullptr) { - ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - if (cppbase::is_empty_or_blank(name)) { ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return nullptr; } - LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); - VTRYRV( auto const id, nullptr, @@ -77,15 +67,10 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { LOG_INFO("deleting blueprint"); - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return; - } + LOG_DEBUG("project={};blueprint={}", blueprint->get_project()->get_path().c_str(), blueprint->get_id()); auto * ctx = blueprint->get_project()->get_function_context(); - LOG_DEBUG("blueprint={}", blueprint->get_id()); - TRYRV( , blueprint->get_project() @@ -101,7 +86,7 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { }) ); - LOG_VERBOSE("blueprint was successfully deleted from database, freeing blueprint"); + LOG_VERBOSE("blueprint was successfully deleted from database, freeing"); blueprint->get_project()->uncache_blueprint(blueprint); @@ -111,11 +96,6 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { LOG_VERBOSE("fetching blueprint property count"); - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return 0; - } - auto * ctx = blueprint->get_project()->get_function_context(); LOG_DEBUG("blueprint={}", blueprint->get_id()); @@ -148,20 +128,15 @@ void ikarus_blueprint_get_properties( ) { LOG_VERBOSE("fetching blueprint properties"); - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return; - } + LOG_DEBUG( + "project={};blueprint={};properties_out_size={}", + blueprint->get_project()->get_path().c_str(), + blueprint->get_id(), + properties_out_size + ); auto * ctx = blueprint->get_project()->get_function_context(); - if (properties_out == nullptr) { - ctx->set_error("properties_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return; - } - - LOG_DEBUG("blueprint={}; properties_out_size={}", blueprint->get_id(), properties_out_size); - IkarusId ids[properties_out_size]; TRYRV( @@ -187,8 +162,23 @@ void ikarus_blueprint_get_properties( LOG_DEBUG("blueprint properties: [{}]", fmt::join(ids, ids + properties_out_size, ", ")); for (size_t i = 0; i < properties_out_size; ++i) { + auto const id = ids[i]; + + VTRYRV( + auto const type, + , + IkarusProperty::get_property_type(blueprint->get_project(), id).on_error([ctx, id](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch property {}'s type: {}", id, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - properties_out[i] = blueprint->get_project()->get_property(ids[i]); + properties_out[i] = blueprint->get_project()->get_property(id, type); } LOG_VERBOSE("successfully fetched blueprint properties"); @@ -197,15 +187,10 @@ void ikarus_blueprint_get_properties( size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { LOG_VERBOSE("fetching blueprint linked entity count"); - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return 0; - } + LOG_DEBUG("project={};blueprint={}", blueprint->get_project()->get_path().c_str(), blueprint->get_id()); auto * ctx = blueprint->get_project()->get_function_context(); - LOG_DEBUG("blueprint={}", blueprint->get_id()); - VTRYRV( auto count, 0, @@ -234,20 +219,15 @@ void ikarus_blueprint_get_linked_entities( ) { LOG_VERBOSE("fetching blueprint linked entities"); - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return; - } + LOG_DEBUG( + "project={};blueprint={};entities_out_size={}", + blueprint->get_project()->get_path().c_str(), + blueprint->get_id(), + entities_out_size + ); auto * ctx = blueprint->get_project()->get_function_context(); - if (entities_out == nullptr) { - ctx->set_error("entities_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return; - } - - LOG_DEBUG("blueprint={}; entities_out_size={}", blueprint->get_id(), entities_out_size); - IkarusId ids[entities_out_size]; TRYRV( @@ -281,22 +261,9 @@ void ikarus_blueprint_get_linked_entities( } IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint) { - return const_cast(ikarus_blueprint_to_object_const(blueprint)); + return static_cast(blueprint); } IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint) { - LOG_VERBOSE("casting blueprint to object"); - - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return nullptr; - } - - // auto * ctx = blueprint->project->function_context(); - - LOG_DEBUG("blueprint={}", blueprint->get_id()); - - LOG_VERBOSE("successfully casted blueprint to object"); - return static_cast(blueprint); } diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index 445efdb..41042ba 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -3,7 +3,7 @@ #include struct IkarusBlueprint final : IkarusObject { - inline IkarusBlueprint(struct IkarusProject * project, IkarusId id); + IkarusBlueprint(struct IkarusProject * project, IkarusId id); IkarusBlueprint(IkarusBlueprint const&) = default; IkarusBlueprint(IkarusBlueprint&&) = default; diff --git a/src/objects/object.cpp b/src/objects/object.cpp index 72aba6e..2ce961e 100644 --- a/src/objects/object.cpp +++ b/src/objects/object.cpp @@ -1,5 +1,9 @@ #include "object.hpp" +IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): + _project{project}, + _id{id} {} + IkarusProject * IkarusObject::get_project() { return _project; } diff --git a/src/objects/object.hpp b/src/objects/object.hpp index ee1b147..bb28da5 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -15,11 +15,11 @@ public: virtual ~IkarusObject() = default; public: - [[nodiscard]] inline struct IkarusProject * get_project(); + [[nodiscard]] struct IkarusProject * get_project(); - [[nodiscard]] inline struct IkarusProject * get_project() const; + [[nodiscard]] struct IkarusProject * get_project() const; - [[nodiscard]] inline IkarusId get_id() const; + [[nodiscard]] IkarusId get_id() const; private: struct IkarusProject mutable * _project; diff --git a/src/objects/properties/number_property.cpp b/src/objects/properties/number_property.cpp index e69de29..d06cfb5 100644 --- a/src/objects/properties/number_property.cpp +++ b/src/objects/properties/number_property.cpp @@ -0,0 +1,4 @@ +#include "number_property.hpp" + +IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, IkarusId id): + IkarusProperty{project, id, this} {} diff --git a/src/objects/properties/number_property.hpp b/src/objects/properties/number_property.hpp index dd5f8a2..df2ab7e 100644 --- a/src/objects/properties/number_property.hpp +++ b/src/objects/properties/number_property.hpp @@ -1,5 +1,8 @@ -// -// Created by Jonathan Purol on 26.11.23. -// +#pragma once -export module number_property.hpp; +#include + +struct IkarusNumberProperty final : IkarusProperty { +public: + IkarusNumberProperty(struct IkarusProject * project, IkarusId id); +}; diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index e69de29..3afcf66 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -0,0 +1,57 @@ +#include "property.hpp" + +#include + +#include + +#include +#include + +IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): + IkarusObject{project, id}, + _data{data} {} + +cppbase::Result IkarusProperty::get_property_type( + IkarusProject * project, IkarusId id +) { + LOG_DEBUG("fetching unboxed property type"); + + LOG_VERBOSE("project={};property={}", project->get_path().c_str(), id); + + VTRY(auto const type, project->get_db()->query_one("SELECT `type` FROM `objects` WHERE `id` = ?", id)); + + return cppbase::ok(static_cast(type)); +} + +IKA_API void ikarus_property_delete(IkarusProperty * property) { + LOG_INFO("deleting property"); + + LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + + auto * ctx = property->get_project()->get_function_context(); + + TRYRV( + , + property->get_project() + ->get_db() + ->execute("DELETE FROM `objects` WHERE `id` = ?", property->get_id()) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to delete property from objects table: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_VERBOSE("property was successfully deleted from database, freeing"); + + property->get_project()->uncache_property(property); + + LOG_VERBOSE("successfully deleted property"); +} + +IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { + LOG_DEBUG("fetching property type"); +} diff --git a/src/objects/properties/property.hpp b/src/objects/properties/property.hpp index 1a74577..b11a8ff 100644 --- a/src/objects/properties/property.hpp +++ b/src/objects/properties/property.hpp @@ -1,9 +1,27 @@ #pragma once +#include + +#include + +#include + +#include + #include struct IkarusProperty : IkarusObject { - IkarusProperty(struct IkarusProject * project, IkarusId id); +public: + using Data = std::variant; + +public: + /// \brief Helper to fetch a type for a property that isn't yet wrapped in an object + [[nodiscard]] static cppbase::Result get_property_type( + struct IkarusProject * project, IkarusId id + ); + +public: + IkarusProperty(struct IkarusProject * project, IkarusId id, Data data); IkarusProperty(IkarusProperty const&) = default; IkarusProperty(IkarusProperty&&) = default; @@ -12,4 +30,7 @@ struct IkarusProperty : IkarusObject { IkarusProperty& operator=(IkarusProperty&&) = default; ~IkarusProperty() override = default; + +private: + Data _data; }; diff --git a/src/objects/properties/text_property.cpp b/src/objects/properties/text_property.cpp index e69de29..fd64154 100644 --- a/src/objects/properties/text_property.cpp +++ b/src/objects/properties/text_property.cpp @@ -0,0 +1,4 @@ +#include "text_property.hpp" + +IkarusTextProperty::IkarusTextProperty(IkarusProject * project, IkarusId id): + IkarusProperty{project, id, this} {} diff --git a/src/objects/properties/text_property.hpp b/src/objects/properties/text_property.hpp index 3816f35..3a5d163 100644 --- a/src/objects/properties/text_property.hpp +++ b/src/objects/properties/text_property.hpp @@ -1,8 +1,8 @@ -// -// Created by Jonathan Purol on 26.11.23. -// +#pragma once -#ifndef TEXT_PROPERTY_HPP -#define TEXT_PROPERTY_HPP +#include -#endif //TEXT_PROPERTY_HPP +struct IkarusTextProperty final : IkarusProperty { +public: + IkarusTextProperty(struct IkarusProject * project, IkarusId id); +}; diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp index e69de29..d2fb82d 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/objects/properties/toggle_property.cpp @@ -0,0 +1,6 @@ +#include "toggle_property.hpp" + +IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): + IkarusProperty{project, id, this} { + +} diff --git a/src/objects/properties/toggle_property.hpp b/src/objects/properties/toggle_property.hpp index 5514d8b..2ca2520 100644 --- a/src/objects/properties/toggle_property.hpp +++ b/src/objects/properties/toggle_property.hpp @@ -3,5 +3,6 @@ #include struct IkarusToggleProperty final : IkarusProperty { +public: IkarusToggleProperty(struct IkarusProject * project, IkarusId id); }; diff --git a/src/persistence/function_context.cpp b/src/persistence/function_context.cpp index 6e252dc..96edddf 100644 --- a/src/persistence/function_context.cpp +++ b/src/persistence/function_context.cpp @@ -14,5 +14,5 @@ FunctionContext::~FunctionContext() { _project->error_infos = {}; } - _project->_function_contexts.pop(); + _project->_function_contexts.pop_back(); } diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index 3ceb821..d7d5e5e 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -2,6 +2,9 @@ #include "ikarus/persistence/project.h" +#include +#include +#include #include auto IkarusProject::get_name() const -> std::string_view { @@ -21,11 +24,11 @@ auto IkarusProject::get_db() const -> sqlitecpp::Connection const * { } auto IkarusProject::get_function_context() -> FunctionContext * { - return &_function_contexts.emplace(this); + return &_function_contexts.emplace_back(this); } IkarusBlueprint * IkarusProject::get_blueprint(IkarusId id) { - return get_cached_object(id, this->_blueprints); + return get_cached_object(id, this->_blueprints); } auto IkarusProject::uncache_blueprint(IkarusBlueprint * blueprint) -> void { @@ -33,15 +36,28 @@ auto IkarusProject::uncache_blueprint(IkarusBlueprint * blueprint) -> void { } auto IkarusProject::get_entity(IkarusId id) -> IkarusEntity * { - return get_cached_object(id, this->_entities); + return get_cached_object(id, this->_entities); } auto IkarusProject::uncache_entity(IkarusEntity * entity) -> void { remove_cached_object(entity, _entities); } -auto IkarusProject::get_property(IkarusId id) -> IkarusProperty * { - return get_cached_object(id, this->_properties); +auto IkarusProject::get_property(IkarusId id, IkarusPropertyType type) -> IkarusProperty * { + auto const iter = _properties.find(id); + + if (iter == _properties.cend()) { + switch (type) { + case IkarusPropertyType_Toggle: + return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); + case IkarusPropertyType_Number: + return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); + case IkarusPropertyType_Text: + return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); + } + } + + return iter->second.get(); } auto IkarusProject::uncache_property(IkarusProperty * property) -> void { diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index ef69a81..d3fc6a1 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -9,6 +9,11 @@ #include #include +#include + +#include +#include +#include constexpr inline auto MAXIMUM_ERROR_INFOS = 8; @@ -32,18 +37,16 @@ public: [[nodiscard]] auto get_entity(IkarusId id) -> struct IkarusEntity *; auto uncache_entity(struct IkarusEntity * entity) -> void; - [[nodiscard]] auto get_property(IkarusId id) -> struct IkarusProperty *; + [[nodiscard]] auto get_property(IkarusId id, IkarusPropertyType type) -> struct IkarusProperty *; auto uncache_property(struct IkarusProperty * property) -> void; private: template - [[nodiscard]] T * get_cached_object(IkarusId id, std::unordered_map>& cache) { + [[nodiscard]] T * get_cached_object(IkarusId id, auto& cache) { auto const iter = cache.find(id); if (iter == cache.cend()) { - auto [ret_iter, _] = cache.emplace(id, std::make_unique(this, id)); - - return ret_iter->second.get(); + return cache.emplace(id, std::make_unique(this, id)).first->second.get(); } return iter->second.get(); @@ -51,7 +54,7 @@ private: template void remove_cached_object(T * object, std::unordered_map>& cache) { - cache.erase(object->id); + cache.erase(object->get_id()); } private: @@ -68,5 +71,5 @@ private: std::unordered_map> _properties; std::unordered_map> _entities; - std::stack _function_contexts; + std::vector _function_contexts; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index f61f2bb..7528ec8 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -9,6 +9,25 @@ #include #include +IkarusValue::IkarusValue(Data data): + _data(data) {} + +bool IkarusValue::is_interminate() const { + return _indeterminate; +} + +void IkarusValue::set_intermediate(bool value) { + _indeterminate = value; +} + +IkarusValue::Data& IkarusValue::get_data() { + return _data; +} + +IkarusValue::Data const& IkarusValue::get_data() const { + return _data; +} + bool ikarus_value_is_indeterminate(IkarusValue const * value) { return value->is_interminate(); } @@ -20,9 +39,9 @@ void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { char const * ikarus_value_to_string(IkarusValue const * value) { auto const str = std::visit( cppbase::overloaded{ - [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->value ? "true" : "false"; }, - [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->value); }, - [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->value); }, + [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->get_value() ? "true" : "false"; }, + [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->get_value()); }, + [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->get_value()); }, }, value->get_data() ); diff --git a/src/values/value.hpp b/src/values/value.hpp index 0adc61c..ddf6601 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -4,11 +4,10 @@ struct IkarusValue { public: - using Data = std::variant::variant; + using Data = std::variant; public: - explicit IkarusValue(Data data): - _data(data) {} + explicit IkarusValue(Data data); IkarusValue(IkarusValue const&) = default; IkarusValue(IkarusValue&&) noexcept = default; @@ -16,24 +15,14 @@ public: IkarusValue& operator=(IkarusValue const&) = default; IkarusValue& operator=(IkarusValue&&) noexcept = default; - virtual ~IkarusValue(); + virtual ~IkarusValue() = default; public: - [[nodiscard]] inline bool is_interminate() const { - return _indeterminate; - } + [[nodiscard]] bool is_interminate() const; + void set_intermediate(bool value); - void set_intermediate(bool value) { - _indeterminate = value; - } - - [[nodiscard]] inline Data& get_data() { - return _data; - } - - [[nodiscard]] inline Data const& get_data() const { - return _data; - } + [[nodiscard]] Data& get_data(); + [[nodiscard]] Data const& get_data() const; private: bool _indeterminate{false}; From b37ca963cde9943910394bd2fa7115b880f998b6 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 27 Nov 2023 13:37:42 +0100 Subject: [PATCH 020/166] implement property (base) logic Signed-off-by: Folling --- include/ikarus/objects/properties/property.h | 18 +-- src/objects/properties/property.cpp | 111 ++++++++++++++++++- src/objects/properties/property.hpp | 4 + 3 files changed, 122 insertions(+), 11 deletions(-) diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index 1da35e6..a6ebad8 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -82,24 +82,24 @@ IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusPro /// \param property The property to visit. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param toggle_property The function to call if the property is a toggle property. Skipped if null. -/// \param number_property The function to call if the property is a number property. Skipped if null. -/// \param text_property The function to call if the property is a text property. Skipped if null. +/// \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. IKA_API void ikarus_property_visit( IkarusProperty * property, - void (*toggle_property)(struct IkarusToggleProperty *, void *), - void (*number_property)(struct IkarusNumberProperty *, void *), - void (*text_property)(struct IkarusTextProperty *, void *), + void (*toggle_property_visitor)(struct IkarusToggleProperty *, void *), + void (*number_property_visitor)(struct IkarusNumberProperty *, void *), + void (*text_property_visitor)(struct IkarusTextProperty *, void *), void * data ); /// \see ikarus_property_visit IKA_API void ikarus_property_visit_const( IkarusProperty const * property, - void (*toggle_property)(struct IkarusToggleProperty const *, void *), - void (*number_property)(struct IkarusNumberProperty const *, void *), - void (*text_property)(struct IkarusTextProperty const *, void *), + 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 ); diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index 3afcf66..cda8434 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -4,13 +4,23 @@ #include +#include #include #include +#include IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): IkarusObject{project, id}, _data{data} {} +IkarusProperty::Data& IkarusProperty::get_data() { + return _data; +} + +IkarusProperty::Data const& IkarusProperty::get_data() const { + return _data; +} + cppbase::Result IkarusProperty::get_property_type( IkarusProject * project, IkarusId id ) { @@ -18,7 +28,21 @@ cppbase::Result IkarusProperty: LOG_VERBOSE("project={};property={}", project->get_path().c_str(), id); - VTRY(auto const type, project->get_db()->query_one("SELECT `type` FROM `objects` WHERE `id` = ?", id)); + auto * ctx = project->get_function_context(); + + VTRY( + auto const type, + project->get_db() + ->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch unboxed property type: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); return cppbase::ok(static_cast(type)); } @@ -53,5 +77,88 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { } IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { - LOG_DEBUG("fetching property type"); + LOG_VERBOSE("fetching property type"); + + return IkarusProperty::get_property_type(property->get_project(), property->get_id()) + .unwrap_value_or(IkarusPropertyType_Toggle); +} + +IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { + LOG_VERBOSE("fetching property source"); + + LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + + auto * ctx = property->get_project()->get_function_context(); + + VTRY( + auto const source, + property->get_project() + ->get_db() + ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->get_id()) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch property's source: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + switch (ikarus_id_get_object_type(source)) { + case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->get_project()->get_blueprint(source)}; + case IkarusObjectType_Entity: return new IkarusPropertySource{property->get_project()->get_entity(source)}; + default: { + ctx->set_error( + fmt::format("PropertySource is neither blueprint nor entity"), + true, + IkarusErrorInfo_Source_LibIkarus, + IkarusErrorInfo_Type_LibIkarus_InvalidState + ); + + return nullptr; + } + } +} + +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 +) { + 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->get_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 +) { + 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->get_data() + ); +} + +IkarusObject * ikarus_property_to_object(IkarusProperty * property) { + return static_cast(property); +} + +IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property) { + return static_cast(property); } diff --git a/src/objects/properties/property.hpp b/src/objects/properties/property.hpp index b11a8ff..6acd377 100644 --- a/src/objects/properties/property.hpp +++ b/src/objects/properties/property.hpp @@ -31,6 +31,10 @@ public: ~IkarusProperty() override = default; +public: + [[nodiscard]] Data& get_data(); + [[nodiscard]] Data const& get_data() const; + private: Data _data; }; From 3697759ceb27d52a777703159f08b7376fa150df Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 28 Nov 2023 11:05:53 +0100 Subject: [PATCH 021/166] make values capable of being a list & add boost Signed-off-by: Folling --- .clang-format | 20 +++---- CMakeLists.txt | 8 +++ include/ikarus/objects/properties/property.h | 8 +++ include/ikarus/values/number_value.h | 49 +++++++++++------ include/ikarus/values/text_value.h | 50 ++++++++++------- include/ikarus/values/toggle_value.h | 49 +++++++++++------ include/ikarus/values/value.h | 11 ++-- src/objects/properties/property.cpp | 26 +++++++++ src/objects/properties/toggle_property.cpp | 6 ++- .../migrations/m1_initial_layout.sql | 17 +++--- src/values/number_value.cpp | 47 ++++++++++++---- src/values/number_value.hpp | 20 +++---- src/values/text_value.cpp | 47 ++++++++++++---- src/values/text_value.hpp | 23 ++++---- src/values/toggle_value.cpp | 54 +++++++++++++++---- src/values/toggle_value.hpp | 20 +++---- 16 files changed, 315 insertions(+), 140 deletions(-) diff --git a/.clang-format b/.clang-format index 513423e..23c71e6 100644 --- a/.clang-format +++ b/.clang-format @@ -87,24 +87,26 @@ IncludeCategories: Priority: 2 - Regex: '^<[a-z0-9_]+>$' Priority: 3 - - Regex: '^$' + - Regex: '^$' Priority: 4 - - Regex: '^$' + - Regex: '^$' Priority: 5 - - Regex: '^$' + - Regex: '^$' Priority: 6 - - Regex: '^$' + - Regex: '^$' Priority: 7 - - Regex: '^$' + - Regex: '^$' Priority: 8 - - Regex: '^$' + - Regex: '^$' Priority: 9 - - Regex: '^$' + - Regex: '^$' Priority: 10 - - Regex: '^$' + - Regex: '^$' Priority: 11 - - Regex: '^$' + - Regex: '^$' Priority: 12 + - Regex: '^$' + Priority: 13 IndentAccessModifiers: false IndentCaseBlocks: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bbb826..ae81667 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,8 @@ add_subdirectory(vendor) add_subdirectory(include) add_subdirectory(src) +find_package(Boost REQUIRED) + add_library( libikarus SHARED ${INCLUDE_FILES} @@ -34,6 +36,12 @@ target_link_libraries( libikarus PRIVATE cppbase sqlitecpp + ${Boost_LIBRARIES} +) + +target_include_directories( + libikarus PRIVATE + ${Boost_INCLUDE_DIRS} ) if (LIBIKARUS_ENABLE_LINTS) diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index a6ebad8..7aa6a2a 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -78,6 +78,14 @@ IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * prope /// \remark Must be freed using #ikarus_free. IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property); +/// \brief Gets the default value of a property. +/// \param property The property to get the type info of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The default value of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); + /// \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. diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index d4b6446..2edb69b 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -4,42 +4,57 @@ /// \author Folling #include +#include /// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER -/// \brief A true/false boolean-like value. For example "IsDead". +/// \brief A true/false long doubleean-esque value. For example "Is Dead". struct IkarusNumberValue; -/// \brief Creates a number value from a long double. -/// \param value The numeric value. -/// \return The entity value. +/// \brief Creates a number value from doubles. +/// \param data The number data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param data_size The size of the data array or 0 if you wish to clear the data. +/// \return The value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create(long double value); +IKA_API IkarusNumberValue * ikarus_number_value_create(long double * data, size_t data_size); /// \brief Creates an indeterminate number value. -/// \return The entity value. +/// \return The value. /// \remark Must be freed with #ikarus_free. IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); -/// \brief Fetches the underlying value of a number value. -/// \param value The number value. -/// \return The underlying value. -/// \warning Undefined if the value is indeterminate. -IKA_API long double ikarus_number_value_get(IkarusNumberValue const * value); - -/// \brief Sets the value of a number value. +/// \brief Fetches the underlying data of a number value. +/// \details You may adjust the returned data as per your hearts desire. +/// If you need to grow the data, use #ikarus_number_value_set with a new array. +/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. +/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. /// \param value The number value. /// \pre \li Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); +/// \param data_size_out An out-parameter for the size of the returned array. +/// \remark Ignored if null. +/// \return The underlying data. Owned by LibIkarus, most not be freed. +/// \warning Undefined if the value is indeterminate. +IKA_API long double * ikarus_number_value_get(IkarusNumberValue * value, size_t * data_size_out); + +/// \see ikarus_number_value_get +long double const * ikarus_number_value_get_const(IkarusNumberValue const * value, size_t * data_size_out); + +/// \brief Sets the data of a number value. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param new_data The new data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, long double * new_data, size_t new_data_size); /// \brief Converts a number value to an entity value. -/// \param number_value The number value to convert. +/// \param value The number value to convert. /// \return The converted entity value. -IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * number_value); +IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value); IKARUS_END_HEADER diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index 8562ab5..3f1f682 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -4,43 +4,57 @@ /// \author Folling #include +#include /// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER -/// \brief A true/false boolean-like value. For example "IsDead". +/// \brief A true/false char const*ean-esque value. For example "Is Dead". struct IkarusTextValue; -/// \brief Creates a text value from a boolean. -/// \param value The text value. -/// \return The entity value. +/// \brief Creates a text value from doubles. +/// \param data The text data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param data_size The size of the data array or 0 if you wish to clear the data. +/// \return The value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create(char const * value); +IKA_API IkarusTextValue * ikarus_text_value_create(char const ** data, size_t data_size); /// \brief Creates an indeterminate text value. -/// \return The entity value. +/// \return The value. /// \remark Must be freed with #ikarus_free. IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); -/// \brief Fetches the underlying value of a text value. -/// \param value The text value. -/// \return The underlying value. -/// \warning Undefined if the value is indeterminate. -/// \remark The value is owned by libikarus and must not be freed. -IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); - -/// \brief Sets the value of a text value. +/// \brief Fetches the underlying data of a text value. +/// \details You may adjust the returned data as per your hearts desire. +/// If you need to grow the data, use #ikarus_text_value_set with a new array. +/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. +/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. /// \param value The text value. /// \pre \li Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); +/// \param data_size_out An out-parameter for the size of the returned array. +/// \remark Ignored if null. +/// \return The underlying data. Owned by LibIkarus, most not be freed. +/// \warning Undefined if the value is indeterminate. +IKA_API char ** ikarus_text_value_get(IkarusTextValue * value, size_t * data_size_out); + +/// \see ikarus_text_value_get +char const * const * ikarus_text_value_get_const(IkarusTextValue const * value, size_t * data_size_out); + +/// \brief Sets the data of a text value. +/// \param value The text value. +/// \pre \li Must not be null. +/// \param new_data The new data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. +IKA_API void ikarus_text_value_set(IkarusTextValue * value, char const ** new_data, size_t new_data_size); /// \brief Converts a text value to an entity value. -/// \param text_value The text value to convert. +/// \param value The text value to convert. /// \return The converted entity value. -IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * text_value); +IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value); IKARUS_END_HEADER diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index fed97d2..89d5678 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -4,42 +4,57 @@ /// \author Folling #include +#include /// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER -/// \brief A true/false boolean-like value. For example "IsDead". +/// \brief A true/false boolean-esque value. For example "Is Dead". struct IkarusToggleValue; -/// \brief Creates a toggle value from a boolean. -/// \param value The toggle value. -/// \return The entity value. +/// \brief Creates a toggle value from doubles. +/// \param data The toggle data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param data_size The size of the data array or 0 if you wish to clear the data. +/// \return The value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value); +IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool * data, size_t data_size); /// \brief Creates an indeterminate toggle value. -/// \return The entity value. +/// \return The value. /// \remark Must be freed with #ikarus_free. IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); -/// \brief Fetches the underlying value of a toggle value. -/// \param value The toggle value. -/// \return The underlying value. -/// \warning Undefined if the value is indeterminate. -IKA_API bool ikarus_toggle_value_get(IkarusToggleValue const * value); - -/// \brief Sets the value of a toggle value. +/// \brief Fetches the underlying data of a toggle value. +/// \details You may adjust the returned data as per your hearts desire. +/// If you need to grow the data, use #ikarus_toggle_value_set with a new array. +/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. +/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. /// \param value The toggle value. /// \pre \li Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); +/// \param data_size_out An out-parameter for the size of the returned array. +/// \remark Ignored if null. +/// \return The underlying data. Owned by LibIkarus, most not be freed. +/// \warning Undefined if the value is indeterminate. +IKA_API bool * ikarus_toggle_value_get(IkarusToggleValue * value, size_t * data_size_out); + +/// \see ikarus_toggle_value_get +bool const * ikarus_toggle_value_get_const(IkarusToggleValue const * value, size_t * data_size_out); + +/// \brief Sets the data of a toggle value. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \param new_data The new data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. +IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool * new_data, size_t new_data_size); /// \brief Converts a toggle value to an entity value. -/// \param toggle_value The toggle value to convert. +/// \param value The toggle value to convert. /// \return The converted entity value. -IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * toggle_value); +IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value); IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index 1cf1fd0..b894701 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -6,11 +6,14 @@ #include /// \defgroup values Values -/// \brief The values stored in entities. +/// \brief The values of properties. /// \details Each entity has a value for each property it is associated with. -/// The value is of the type specified by the property and constrained by the property's settings. -/// A value may be indeterminate which means it is unknown or not specified. -/// \see PropertyType PropertySettings +/// These value classes represent plain objects. They are not associated with any entity. +/// Each value may be indeterminate. \see IkarusProperty +/// Values are stored as lists. If a property is "singular" then its value is a list of size 1. +/// Values are typed, with types existing for each of the corresponding property types. +/// When setting values for a property the type must match the property type and the value must be valid under the property's +/// settings. \see PropertyType /// @{ IKARUS_BEGIN_HEADER diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index cda8434..d1eb94a 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -8,6 +8,7 @@ #include #include #include +#include IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): IkarusObject{project, id}, @@ -121,6 +122,31 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p } } +IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) { + LOG_VERBOSE("fetching property default value"); + + LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + + auto * ctx = property->get_project()->get_function_context(); + + VTRY( + auto const value, + property->get_project() + ->get_db() + ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->get_id()) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch property's default value: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + return new IkarusValue(property->get_project(), value); +} + void ikarus_property_visit( IkarusProperty * property, void (*toggle_property_visitor)(struct IkarusToggleProperty *, void *), diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp index d2fb82d..70ec934 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/objects/properties/toggle_property.cpp @@ -1,6 +1,10 @@ #include "toggle_property.hpp" IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): - IkarusProperty{project, id, this} { + IkarusProperty{project, id, this} {} + +IkarusToggleProperty * ikarus_toggle_property_create( + struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source +) { } diff --git a/src/persistence/migrations/m1_initial_layout.sql b/src/persistence/migrations/m1_initial_layout.sql index d94fa10..ea24c56 100644 --- a/src/persistence/migrations/m1_initial_layout.sql +++ b/src/persistence/migrations/m1_initial_layout.sql @@ -1,8 +1,9 @@ CREATE TABLE `objects` ( `do_not_access_rowid_alias` INTEGER PRIMARY KEY, - `object_type` INT NOT NULL, - `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56)) VIRTUAL, + `object_type` INT NOT NULL, + `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56) +) VIRTUAL, `name` TEXT NOT NULL, `information` TEXT NOT NULL ) STRICT; @@ -11,7 +12,7 @@ CREATE UNIQUE INDEX `object_id` ON `objects` (`id`); CREATE INDEX `object_type` ON `objects` (`object_type`); CREATE - VIRTUAL TABLE `objects_fts` USING fts5 +VIRTUAL TABLE `objects_fts` USING fts5 ( `name`, `information`, @@ -23,7 +24,7 @@ CREATE "unicode61 remove_diacritics 2 tokenchars '-_'" ); -CREATE TABLE `blueprints` +CREATE TABLE `entities` ( `id` INT, @@ -31,7 +32,7 @@ CREATE TABLE `blueprints` FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; -CREATE TABLE `entities` +CREATE TABLE `blueprints` ( `id` INT, @@ -69,7 +70,7 @@ CREATE INDEX `properties_type` ON `properties` (`type`); CREATE INDEX `properties_source` ON `properties` (`source`); CREATE - VIRTUAL TABLE `property_default_value_fts` USING fts5 +VIRTUAL TABLE `property_default_value_fts` USING fts5 ( `default_value`, content= @@ -81,7 +82,7 @@ CREATE ); CREATE - VIRTUAL TABLE `property_settings_fts` USING fts5 +VIRTUAL TABLE `property_settings_fts` USING fts5 ( `settings`, content= @@ -104,7 +105,7 @@ CREATE TABLE `values` ) WITHOUT ROWID, STRICT; CREATE - VIRTUAL TABLE `values_fts` USING fts5 +VIRTUAL TABLE `values_fts` USING fts5 ( `value`, content= diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp index b4d3413..4a450e3 100644 --- a/src/values/number_value.cpp +++ b/src/values/number_value.cpp @@ -2,25 +2,54 @@ #include -IkarusNumberValue * ikarus_number_value_create(long double value) { - return new IkarusNumberValue{value}; +IkarusNumberValue::IkarusNumberValue(): + IkarusValue{this} {} + +boost::container::vector& IkarusNumberValue::get_value() { + return _value; +} + +boost::container::vector const& IkarusNumberValue::get_value() const { + return _value; +} + +IkarusNumberValue * ikarus_number_value_create(long double * data, size_t data_size) { + auto * ret = new IkarusNumberValue{}; + + ikarus_number_value_set(ret, data, data_size); + + return ret; } IkarusNumberValue * ikarus_number_value_create_indeterminate() { - auto * ret = new IkarusNumberValue{0.0}; + auto * ret = new IkarusNumberValue{}; ret->set_intermediate(true); return ret; } -long double ikarus_number_value_get(IkarusNumberValue const * value) { - return value->get_value(); +long double * ikarus_number_value_get(IkarusNumberValue * value, size_t * data_size_out) { + // NOLINTNEXTLINE(*-pro-type-const-cast) + return const_cast(ikarus_number_value_get_const(value, data_size_out)); } -void ikarus_number_value_set(IkarusNumberValue * value, long double new_value) { - value->set_value(new_value); +long double const * ikarus_number_value_get_const(IkarusNumberValue const * value, size_t * data_size_out) { + if (data_size_out != nullptr) { + *data_size_out = value->get_value().size(); + } + + return value->get_value().data(); } -struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * number_value) { - return static_cast(number_value); +void ikarus_number_value_set(IkarusNumberValue * value, long double * new_data, size_t new_data_size) { + value->get_value().reserve(new_data_size); + + for (auto i = 0; i < new_data_size; ++i) { + // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) + value->get_value().emplace_back(new_data[i]); + } +} + +struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value) { + return static_cast(value); } diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index e495be5..f6befe1 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -1,13 +1,14 @@ #pragma once +#include + +#include + #include -/// \private struct IkarusNumberValue final : IkarusValue { public: - explicit IkarusNumberValue(long double value): - IkarusValue{this}, - _value{value} {} + explicit IkarusNumberValue(); IkarusNumberValue(IkarusNumberValue const&) = default; IkarusNumberValue(IkarusNumberValue&&) = default; @@ -18,14 +19,9 @@ public: ~IkarusNumberValue() override = default; public: - [[nodiscard]] long double get_value() const { - return _value; - } - - void set_value(long double value) { - _value = value; - } + [[nodiscard]] boost::container::vector& get_value(); + [[nodiscard]] boost::container::vector const& get_value() const; private: - long double _value; + boost::container::vector _value{}; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp index 6eb839c..ba30eb8 100644 --- a/src/values/text_value.cpp +++ b/src/values/text_value.cpp @@ -1,26 +1,55 @@ #include "ikarus/values/text_value.h" -#include "text_value.hpp" +#include -IkarusTextValue * ikarus_text_value_create(char const * value) { - return new IkarusTextValue{value}; +IkarusTextValue::IkarusTextValue(): + IkarusValue{this} {} + +boost::container::vector& IkarusTextValue::get_value() { + return _value; +} + +boost::container::vector const& IkarusTextValue::get_value() const { + return _value; +} + +IkarusTextValue * ikarus_text_value_create(char const ** data, size_t data_size) { + auto * ret = new IkarusTextValue{}; + + ikarus_text_value_set(ret, data, data_size); + + return ret; } IkarusTextValue * ikarus_text_value_create_indeterminate() { - auto * ret = new IkarusTextValue{""}; + auto * ret = new IkarusTextValue{}; ret->set_intermediate(true); return ret; } -char const * ikarus_text_value_get(IkarusTextValue const * value) { +char ** ikarus_text_value_get(IkarusTextValue * value, size_t * data_size_out) { + // NOLINTNEXTLINE(*-pro-type-const-cast) + return const_cast(ikarus_text_value_get_const(value, data_size_out)); +} + +char const * const * ikarus_text_value_get_const(IkarusTextValue const * value, size_t * data_size_out) { + if (data_size_out != nullptr) { + *data_size_out = value->get_value().size(); + } + return value->get_value().data(); } -void ikarus_text_value_set(IkarusTextValue * value, char const * new_value) { - value->set_value(new_value); +void ikarus_text_value_set(IkarusTextValue * value, char const ** new_data, size_t new_data_size) { + value->get_value().reserve(new_data_size); + + for (auto i = 0; i < new_data_size; ++i) { + // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) + value->get_value().emplace_back(new_data[i]); + } } -struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * text_value) { - return static_cast(text_value); +struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value) { + return static_cast(value); } diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 3766513..42484a2 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -1,33 +1,28 @@ #pragma once #include +#include + +#include #include -/// \private struct IkarusTextValue final : IkarusValue { public: - explicit IkarusTextValue(std::string value): - IkarusValue{this}, - _value(std::move(value)) {} + explicit IkarusTextValue(); IkarusTextValue(IkarusTextValue const&) = default; - IkarusTextValue(IkarusTextValue&&) noexcept = default; + IkarusTextValue(IkarusTextValue&&) = default; IkarusTextValue& operator=(IkarusTextValue const&) = default; - IkarusTextValue& operator=(IkarusTextValue&&) noexcept = default; + IkarusTextValue& operator=(IkarusTextValue&&) = default; ~IkarusTextValue() override = default; public: - [[nodiscard]] std::string_view get_value() const noexcept { - return _value; - } - - void set_value(std::string_view value) { - _value = value; - } + [[nodiscard]] boost::container::vector& get_value(); + [[nodiscard]] boost::container::vector const& get_value() const; private: - std::string _value; + boost::container::vector _value{}; }; diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp index 30e64c8..c03f71b 100644 --- a/src/values/toggle_value.cpp +++ b/src/values/toggle_value.cpp @@ -1,26 +1,60 @@ #include "ikarus/values/toggle_value.h" -#include "toggle_value.hpp" +#include -IkarusToggleValue * ikarus_toggle_value_create(bool value) { - return new IkarusToggleValue{value}; +IkarusToggleValue::IkarusToggleValue(): + IkarusValue{this} {} + +boost::container::vector& IkarusToggleValue::get_value() { + return _value; +} + +boost::container::vector const& IkarusToggleValue::get_value() const { + return _value; +} + +IkarusToggleValue * ikarus_toggle_value_create(bool * data, size_t data_size) { + auto * ret = new IkarusToggleValue{}; + + ikarus_toggle_value_set(ret, data, data_size); + + return ret; } IkarusToggleValue * ikarus_toggle_value_create_indeterminate() { - auto * ret = new IkarusToggleValue{false}; + auto * ret = new IkarusToggleValue{}; ret->set_intermediate(true); return ret; } -bool ikarus_toggle_value_get(IkarusToggleValue const * value) { - return value->get_value(); +bool * ikarus_toggle_value_get(IkarusToggleValue * value, size_t * data_size_out) { + // NOLINTNEXTLINE(*-pro-type-const-cast) + return const_cast(ikarus_toggle_value_get_const(value, data_size_out)); } -void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value) { - value->set_value(new_value); +bool const * ikarus_toggle_value_get_const(IkarusToggleValue const * value, size_t * data_size_out) { + if (data_size_out != nullptr) { + *data_size_out = value->get_value().size(); + } + + return value->get_value().data(); } -struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * toggle_value) { - return static_cast(toggle_value); +void ikarus_toggle_value_set(IkarusToggleValue * value, bool * new_data, size_t new_data_size) { + if (new_data == nullptr || new_data_size == 0) { + value->get_value().clear(); + return; + } + + value->get_value().reserve(new_data_size); + + for (auto i = 0; i < new_data_size; ++i) { + // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) + value->get_value().emplace_back(new_data[i]); + } +} + +struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value) { + return static_cast(value); } diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index b526476..81386a2 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -1,13 +1,14 @@ #pragma once +#include + +#include + #include -/// \private struct IkarusToggleValue final : IkarusValue { public: - explicit IkarusToggleValue(bool value): - IkarusValue{this}, - _value{value} {} + explicit IkarusToggleValue(); IkarusToggleValue(IkarusToggleValue const&) = default; IkarusToggleValue(IkarusToggleValue&&) = default; @@ -18,14 +19,9 @@ public: ~IkarusToggleValue() override = default; public: - [[nodiscard]] bool get_value() const { - return _value; - } - - void set_value(bool value) { - _value = value; - } + [[nodiscard]] boost::container::vector& get_value(); + [[nodiscard]] boost::container::vector const& get_value() const; private: - bool _value; + boost::container::vector _value{}; }; From 643ece4342dd41a18b3bfce11ce0b8888030c970 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 3 Dec 2023 10:16:13 +0100 Subject: [PATCH 022/166] adjust values api to vector-esque interface Signed-off-by: Folling --- include/ikarus/values/number_value.h | 61 ++++++++++------- include/ikarus/values/text_value.h | 56 +++++++++------- include/ikarus/values/toggle_value.h | 66 +++++++++++-------- src/values/number_value.cpp | 48 ++++---------- src/values/number_value.hpp | 11 ++-- src/values/text_value.cpp | 48 ++++---------- src/values/text_value.hpp | 12 ++-- src/values/toggle_value.cpp | 53 ++++----------- src/values/toggle_value.hpp | 11 ++-- src/values/value.cpp | 97 +++++++++++++++++++++++++--- src/values/value.hpp | 17 +++-- src/values/value_base.hpp | 35 ++++++++++ vendor/sqlitecpp | 2 +- 13 files changed, 297 insertions(+), 220 deletions(-) create mode 100644 src/values/value_base.hpp diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index 2edb69b..2bdeb85 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -11,14 +11,14 @@ IKARUS_BEGIN_HEADER -/// \brief A true/false long doubleean-esque value. For example "Is Dead". +/// \brief A numeric value. For example "Age" or "Height". struct IkarusNumberValue; -/// \brief Creates a number value from doubles. -/// \param data The number data or null if you wish to clear the data. +/// \brief Creates a number value from long doubles. +/// \param data The number data or null if you wish to create an empty value. /// \details LibIkarus does not take ownership of this array. -/// \param data_size The size of the data array or 0 if you wish to clear the data. -/// \return The value. +/// \param data_size The size of the data array. +/// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. IKA_API IkarusNumberValue * ikarus_number_value_create(long double * data, size_t data_size); @@ -27,29 +27,44 @@ IKA_API IkarusNumberValue * ikarus_number_value_create(long double * data, size_ /// \remark Must be freed with #ikarus_free. IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); -/// \brief Fetches the underlying data of a number value. -/// \details You may adjust the returned data as per your hearts desire. -/// If you need to grow the data, use #ikarus_number_value_set with a new array. -/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. -/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. +/// \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 data_size_out An out-parameter for the size of the returned array. -/// \remark Ignored if null. -/// \return The underlying data. Owned by LibIkarus, most not be freed. -/// \warning Undefined if the value is indeterminate. -IKA_API long double * ikarus_number_value_get(IkarusNumberValue * value, size_t * data_size_out); +/// \param idx The index of the data to fetch. +/// \pre \li Must be less than the size of the value. +/// \return The underlying data or null if an error occurs or the value is indeterminate. +IKA_API long double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx); -/// \see ikarus_number_value_get -long double const * ikarus_number_value_get_const(IkarusNumberValue const * value, size_t * data_size_out); - -/// \brief Sets the data of a number value. +/// \brief Fetches the size of the underlying data of a number value. /// \param value The number value. /// \pre \li Must not be null. -/// \param new_data The new data or null if you wish to clear the data. -/// \details LibIkarus does not take ownership of this array. -/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, long double * new_data, size_t new_data_size); +/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); + +/// \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. +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double new_data); + +/// \brief Removes a data from a number value. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param idx The index of the data to remove. +/// \pre \li Must be less than the size of the value. +/// \remark This will shift all data after the index by one to the left. +IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); + +/// \brief Inserts a data into a number value. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param idx The index of the data to insert. +/// \pre \li Must be less than or equal to the size of the value. +/// \param new_data The new data. +/// \remark This will shift all data after the index by one to the right. +IKA_API void ikarus_number_insert(IkarusNumberValue * value, size_t idx, long double new_data); /// \brief Converts a number value to an entity value. /// \param value The number value to convert. diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index 3f1f682..08a37c8 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -11,14 +11,14 @@ IKARUS_BEGIN_HEADER -/// \brief A true/false char const*ean-esque value. For example "Is Dead". +/// \brief A textual value. For example "Surname" or "Description". struct IkarusTextValue; -/// \brief Creates a text value from doubles. -/// \param data The text data or null if you wish to clear the data. +/// \brief Creates a text value from strings. +/// \param data The text data or null if you wish to create an empty value. /// \details LibIkarus does not take ownership of this array. -/// \param data_size The size of the data array or 0 if you wish to clear the data. -/// \return The value. +/// \param data_size The size of the data array. +/// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. IKA_API IkarusTextValue * ikarus_text_value_create(char const ** data, size_t data_size); @@ -27,29 +27,39 @@ IKA_API IkarusTextValue * ikarus_text_value_create(char const ** data, size_t da /// \remark Must be freed with #ikarus_free. IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); -/// \brief Fetches the underlying data of a text value. -/// \details You may adjust the returned data as per your hearts desire. -/// If you need to grow the data, use #ikarus_text_value_set with a new array. -/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. -/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. +/// \see ikarus_number_value_get +IKA_API char const * const * ikarus_text_value_get(IkarusTextValue const * value, size_t idx); + +/// \brief Fetches the size of the underlying data of a text value. /// \param value The text value. /// \pre \li Must not be null. -/// \param data_size_out An out-parameter for the size of the returned array. -/// \remark Ignored if null. -/// \return The underlying data. Owned by LibIkarus, most not be freed. -/// \warning Undefined if the value is indeterminate. -IKA_API char ** ikarus_text_value_get(IkarusTextValue * value, size_t * data_size_out); +/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value); -/// \see ikarus_text_value_get -char const * const * ikarus_text_value_get_const(IkarusTextValue const * value, size_t * data_size_out); - -/// \brief Sets the data of a text value. +/// \brief Sets the data of a text value at a specific index. /// \param value The text value. /// \pre \li Must not be null. -/// \param new_data The new data or null if you wish to clear the data. -/// \details LibIkarus does not take ownership of this array. -/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. -IKA_API void ikarus_text_value_set(IkarusTextValue * value, char const ** new_data, size_t new_data_size); +/// \param idx The index of the data to set. +/// \pre \li Must be less than the size of the value. +/// \param new_data The new data. +IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data); + +/// \brief Removes a data from a text value. +/// \param value The text value. +/// \pre \li Must not be null. +/// \param idx The index of the data to remove. +/// \pre \li Must be less than the size of the value. +/// \remark This will shift all data after the index by one to the left. +IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx); + +/// \brief Inserts a data into a text value. +/// \param value The text value. +/// \pre \li Must not be null. +/// \param idx The index of the data to insert. +/// \pre \li Must be less than or equal to the size of the value. +/// \param new_data The new data. +/// \remark This will shift all data after the index by one to the right. +IKA_API void ikarus_text_insert(IkarusTextValue * value, size_t idx, char const * new_data); /// \brief Converts a text value to an entity value. /// \param value The text value to convert. diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index 89d5678..2fe45d3 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -14,42 +14,52 @@ IKARUS_BEGIN_HEADER /// \brief A true/false boolean-esque value. For example "Is Dead". struct IkarusToggleValue; -/// \brief Creates a toggle value from doubles. -/// \param data The toggle data or null if you wish to clear the data. -/// \details LibIkarus does not take ownership of this array. -/// \param data_size The size of the data array or 0 if you wish to clear the data. -/// \return The value. +/// \brief Creates a toggle value from booleans. +/// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool * data, size_t data_size); +IKA_API IkarusToggleValue * ikarus_toggle_value_create(); -/// \brief Creates an indeterminate toggle value. -/// \return The value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); +/// \brief Fetches the underlying data of a number value at a specific index. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param idx The index of the data to fetch. +/// \pre \li Must be less than the size of the value. +/// \return The underlying data or null if an error occurs or the value is indeterminate. +IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue * value, size_t idx); -/// \brief Fetches the underlying data of a toggle value. -/// \details You may adjust the returned data as per your hearts desire. -/// If you need to grow the data, use #ikarus_toggle_value_set with a new array. -/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. -/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. +/// \brief Fetches the size of the underlying data of a toggle value. /// \param value The toggle value. /// \pre \li Must not be null. -/// \param data_size_out An out-parameter for the size of the returned array. -/// \remark Ignored if null. -/// \return The underlying data. Owned by LibIkarus, most not be freed. -/// \warning Undefined if the value is indeterminate. -IKA_API bool * ikarus_toggle_value_get(IkarusToggleValue * value, size_t * data_size_out); +/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value); -/// \see ikarus_toggle_value_get -bool const * ikarus_toggle_value_get_const(IkarusToggleValue const * value, size_t * data_size_out); - -/// \brief Sets the data of a toggle value. +/// \brief Sets the data of a toggle value at a specific index. /// \param value The toggle value. /// \pre \li Must not be null. -/// \param new_data The new data or null if you wish to clear the data. -/// \details LibIkarus does not take ownership of this array. -/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. -IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool * new_data, size_t new_data_size); +/// \param idx The index of the data to set. +/// \pre \li Must be less than the size of the value. +/// \param new_data The new data. +IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data); + +/// \brief Removes a data from a toggle value. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \param idx The index of the data to remove. +/// \pre \li Must be less than the size of the value. +IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx); + +/// \brief Inserts a data into a toggle value. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \param idx The index of the data to insert. +/// \pre \li Must be less than or equal to the size of the value. +/// \param new_data The new data. +IKA_API void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data); + +/// \brief Clears a toggle value. +/// \param value The toggle value. +/// \remark Noop if the value is indeterminate. +IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value); /// \brief Converts a toggle value to an entity value. /// \param value The toggle value to convert. diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp index 4a450e3..c928b10 100644 --- a/src/values/number_value.cpp +++ b/src/values/number_value.cpp @@ -1,55 +1,35 @@ #include "ikarus/values/number_value.h" #include +#include IkarusNumberValue::IkarusNumberValue(): IkarusValue{this} {} -boost::container::vector& IkarusNumberValue::get_value() { - return _value; +IkarusNumberValue * ikarus_number_value_create() { + return new IkarusNumberValue{}; } -boost::container::vector const& IkarusNumberValue::get_value() const { - return _value; +long double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx) { + return ikarus_value_base_get(value, idx); } -IkarusNumberValue * ikarus_number_value_create(long double * data, size_t data_size) { - auto * ret = new IkarusNumberValue{}; - - ikarus_number_value_set(ret, data, data_size); - - return ret; +size_t ikarus_number_value_get_size(IkarusNumberValue const * value) { + return ikarus_value_base_get_size(value); } -IkarusNumberValue * ikarus_number_value_create_indeterminate() { - auto * ret = new IkarusNumberValue{}; - ret->set_intermediate(true); - - return ret; +void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double new_data) { + return ikarus_value_base_set(value, idx, new_data); } -long double * ikarus_number_value_get(IkarusNumberValue * value, size_t * data_size_out) { - // NOLINTNEXTLINE(*-pro-type-const-cast) - return const_cast(ikarus_number_value_get_const(value, data_size_out)); +void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx) { + return ikarus_value_base_remove(value, idx); } -long double const * ikarus_number_value_get_const(IkarusNumberValue const * value, size_t * data_size_out) { - if (data_size_out != nullptr) { - *data_size_out = value->get_value().size(); - } - - return value->get_value().data(); -} - -void ikarus_number_value_set(IkarusNumberValue * value, long double * new_data, size_t new_data_size) { - value->get_value().reserve(new_data_size); - - for (auto i = 0; i < new_data_size; ++i) { - // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) - value->get_value().emplace_back(new_data[i]); - } +void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, long double new_data) { + return ikarus_value_base_insert(value, idx, new_data); } struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value) { - return static_cast(value); + return ikarus_value_base_to_value(value); } diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index f6befe1..e1b6915 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -1,12 +1,13 @@ #pragma once -#include - #include #include struct IkarusNumberValue final : IkarusValue { +public: + using data_type = long double; + public: explicit IkarusNumberValue(); @@ -19,9 +20,5 @@ public: ~IkarusNumberValue() override = default; public: - [[nodiscard]] boost::container::vector& get_value(); - [[nodiscard]] boost::container::vector const& get_value() const; - -private: - boost::container::vector _value{}; + boost::container::vector value{}; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp index ba30eb8..a382c0a 100644 --- a/src/values/text_value.cpp +++ b/src/values/text_value.cpp @@ -1,55 +1,35 @@ #include "ikarus/values/text_value.h" #include +#include IkarusTextValue::IkarusTextValue(): IkarusValue{this} {} -boost::container::vector& IkarusTextValue::get_value() { - return _value; +IkarusTextValue * ikarus_text_value_create() { + return new IkarusTextValue{}; } -boost::container::vector const& IkarusTextValue::get_value() const { - return _value; +char const * ikarus_text_value_get(IkarusTextValue * value, size_t idx) { + return ikarus_value_base_get(value, idx)->data(); } -IkarusTextValue * ikarus_text_value_create(char const ** data, size_t data_size) { - auto * ret = new IkarusTextValue{}; - - ikarus_text_value_set(ret, data, data_size); - - return ret; +size_t ikarus_text_value_get_size(IkarusTextValue const * value) { + return ikarus_value_base_get_size(value); } -IkarusTextValue * ikarus_text_value_create_indeterminate() { - auto * ret = new IkarusTextValue{}; - ret->set_intermediate(true); - - return ret; +void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data) { + return ikarus_value_base_set(value, idx, new_data); } -char ** ikarus_text_value_get(IkarusTextValue * value, size_t * data_size_out) { - // NOLINTNEXTLINE(*-pro-type-const-cast) - return const_cast(ikarus_text_value_get_const(value, data_size_out)); +void ikarus_text_value_remove(IkarusTextValue * value, size_t idx) { + return ikarus_value_base_remove(value, idx); } -char const * const * ikarus_text_value_get_const(IkarusTextValue const * value, size_t * data_size_out) { - if (data_size_out != nullptr) { - *data_size_out = value->get_value().size(); - } - - return value->get_value().data(); -} - -void ikarus_text_value_set(IkarusTextValue * value, char const ** new_data, size_t new_data_size) { - value->get_value().reserve(new_data_size); - - for (auto i = 0; i < new_data_size; ++i) { - // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) - value->get_value().emplace_back(new_data[i]); - } +void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data) { + return ikarus_value_base_insert(value, idx, new_data); } struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value) { - return static_cast(value); + return ikarus_value_base_to_value(value); } diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 42484a2..9596a48 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -1,13 +1,13 @@ #pragma once -#include -#include - #include #include struct IkarusTextValue final : IkarusValue { +public: + using data_type = std::string; + public: explicit IkarusTextValue(); @@ -20,9 +20,5 @@ public: ~IkarusTextValue() override = default; public: - [[nodiscard]] boost::container::vector& get_value(); - [[nodiscard]] boost::container::vector const& get_value() const; - -private: - boost::container::vector _value{}; + boost::container::vector value{}; }; diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp index c03f71b..8d11e5a 100644 --- a/src/values/toggle_value.cpp +++ b/src/values/toggle_value.cpp @@ -1,60 +1,35 @@ #include "ikarus/values/toggle_value.h" #include +#include IkarusToggleValue::IkarusToggleValue(): IkarusValue{this} {} -boost::container::vector& IkarusToggleValue::get_value() { - return _value; +IkarusToggleValue * ikarus_toggle_value_create() { + return new IkarusToggleValue{}; } -boost::container::vector const& IkarusToggleValue::get_value() const { - return _value; +bool const * ikarus_toggle_value_get(IkarusToggleValue * value, size_t idx) { + return ikarus_value_base_get(value, idx); } -IkarusToggleValue * ikarus_toggle_value_create(bool * data, size_t data_size) { - auto * ret = new IkarusToggleValue{}; - - ikarus_toggle_value_set(ret, data, data_size); - - return ret; +size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value) { + return ikarus_value_base_get_size(value); } -IkarusToggleValue * ikarus_toggle_value_create_indeterminate() { - auto * ret = new IkarusToggleValue{}; - ret->set_intermediate(true); - - return ret; +void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data) { + return ikarus_value_base_set(value, idx, new_data); } -bool * ikarus_toggle_value_get(IkarusToggleValue * value, size_t * data_size_out) { - // NOLINTNEXTLINE(*-pro-type-const-cast) - return const_cast(ikarus_toggle_value_get_const(value, data_size_out)); +void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx) { + return ikarus_value_base_remove(value, idx); } -bool const * ikarus_toggle_value_get_const(IkarusToggleValue const * value, size_t * data_size_out) { - if (data_size_out != nullptr) { - *data_size_out = value->get_value().size(); - } - - return value->get_value().data(); -} - -void ikarus_toggle_value_set(IkarusToggleValue * value, bool * new_data, size_t new_data_size) { - if (new_data == nullptr || new_data_size == 0) { - value->get_value().clear(); - return; - } - - value->get_value().reserve(new_data_size); - - for (auto i = 0; i < new_data_size; ++i) { - // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) - value->get_value().emplace_back(new_data[i]); - } +void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data) { + return ikarus_value_base_insert(value, idx, new_data); } struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value) { - return static_cast(value); + return ikarus_value_base_to_value(value); } diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index 81386a2..cb8d110 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -1,12 +1,13 @@ #pragma once -#include - #include #include struct IkarusToggleValue final : IkarusValue { +public: + using data_type = bool; + public: explicit IkarusToggleValue(); @@ -19,9 +20,5 @@ public: ~IkarusToggleValue() override = default; public: - [[nodiscard]] boost::container::vector& get_value(); - [[nodiscard]] boost::container::vector const& get_value() const; - -private: - boost::container::vector _value{}; + boost::container::vector value{}; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index 7528ec8..e0e9355 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -1,9 +1,13 @@ #include "ikarus/values/value.h" +#include + #include #include +#include + #include #include #include @@ -12,11 +16,90 @@ IkarusValue::IkarusValue(Data data): _data(data) {} -bool IkarusValue::is_interminate() const { +cppbase::Result IkarusValue::from_json(boost::json::value const& json) { + bool const * intermediate = nullptr; + boost::int64_t const * type = nullptr; + boost::json::array const * data = nullptr; + + if (auto const * obj = json.if_object(); obj == nullptr) { + return cppbase::err(FromJsonError{}); + } else { + if (auto const * type_value = obj->if_contains("type"); type_value == nullptr) { + return cppbase::err(FromJsonError{}); + } else if (type = type_value->if_int64(); type == nullptr) { + return cppbase::err(FromJsonError{}); + } + + if (auto const * intermediate_value = obj->if_contains("intermediate"); intermediate_value == nullptr) { + return cppbase::err(FromJsonError{}); + } else if (intermediate = intermediate_value->if_bool(); intermediate == nullptr) { + return cppbase::err(FromJsonError{}); + } + + if (auto const * data = obj->if_contains("data"); data == nullptr) { + return cppbase::err(FromJsonError{}); + } else if (auto * array = data->if_array(); array == nullptr) { + return cppbase::err(FromJsonError{}); + } + + auto assign_value = + []( + auto const& value, boost::json::array const& array, auto convert = [](auto const& val) { return val; } + ) { + for (auto const& data : array) { + if (auto res = boost::json::try_value_to(data); res.has_error()) { + return cppbase::err(FromJsonError{}); + } else { + value->get_data().push_back(convert(res.value())); + return cppbase::ok(); + } + } + }; + + switch (*type) { + case IkarusPropertyType_Toggle: { + auto * ret = new IkarusToggleValue{}; + ret->set_intermediate(*intermediate); + + assign_value.operator()(ret, *data); + } + case IkarusPropertyType_Number: { + auto * ret = new IkarusToggleValue{}; + ret->set_intermediate(*intermediate); + + assign_value.operator()(ret, *data); + } + case IkarusPropertyType_Text: { + auto * ret = new IkarusToggleValue{}; + ret->set_intermediate(*intermediate); + + assign_value.operator()(ret, *data); + } + default: return cppbase::err(FromJsonError{}); + } + } +} + +boost::json::value IkarusValue::to_json() const { + return { + {"indeterminate", _indeterminate}, + {"type", + std::visit( + cppbase::overloaded{ + []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusPropertyType_Toggle; }, + []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusPropertyType_Number; }, + []([[maybe_unused]] IkarusTextValue const * value) { return IkarusPropertyType_Text; }, + }, _data + )}, + {"data", std::visit([](auto const * value) { return boost::json::value_from(value->get_data()); }, _data)} + }; +} + +bool IkarusValue::is_indeterminate() const { return _indeterminate; } -void IkarusValue::set_intermediate(bool value) { +void IkarusValue::set_indeterminate(bool value) { _indeterminate = value; } @@ -29,7 +112,7 @@ IkarusValue::Data const& IkarusValue::get_data() const { } bool ikarus_value_is_indeterminate(IkarusValue const * value) { - return value->is_interminate(); + return value->is_indeterminate(); } void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { @@ -39,18 +122,14 @@ void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { char const * ikarus_value_to_string(IkarusValue const * value) { auto const str = std::visit( cppbase::overloaded{ - [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->get_value() ? "true" : "false"; }, + [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->get_data() ? "true" : "false"; }, [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->get_value()); }, [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->get_value()); }, }, value->get_data() ); - auto * const ret = new char[str.size() + 1]; - - std::strncpy(ret, str.data(), str.size() + 1); - - return ret; + return strdup(str.data()); } void ikarus_value_visit( diff --git a/src/values/value.hpp b/src/values/value.hpp index ddf6601..c58d9c7 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -2,6 +2,10 @@ #include +#include + +#include + struct IkarusValue { public: using Data = std::variant; @@ -18,13 +22,12 @@ public: virtual ~IkarusValue() = default; public: - [[nodiscard]] bool is_interminate() const; - void set_intermediate(bool value); + struct FromJsonError {}; - [[nodiscard]] Data& get_data(); - [[nodiscard]] Data const& get_data() const; + [[nodiscard]] static cppbase::Result from_json(boost::json::value const& json); + [[nodiscard]] boost::json::value to_json() const; -private: - bool _indeterminate{false}; - Data _data; +public: + bool indeterminate{false}; + Data data; }; diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp new file mode 100644 index 0000000..7fbb589 --- /dev/null +++ b/src/values/value_base.hpp @@ -0,0 +1,35 @@ +#pragma once + +template +typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { + if (value->is_indeterminate()) { + return nullptr; + } + + return &value->value[idx]; +} + +template +size_t ikarus_value_base_get_size(V const * value) { + return value->value.size(); +} + +template +void ikarus_value_base_set(V * value, size_t idx, typename V::data_type new_data) { + value->value[idx] = new_data; +} + +template +void ikarus_value_base_remove(V * value, size_t idx) { + value->value.erase(value->value.begin() + idx); +} + +template +void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_data) { + value->value.insert(value->value.begin() + idx, new_data); +} + +template +struct IkarusValue * ikarus_value_base_to_value(V * value) { + return static_cast(value); +} diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 07b806f..6e39dd2 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 07b806f4d73b27a555a3ba82a97d89913e430e48 +Subproject commit 6e39dd241f8469784b8a356e7a9563fa58b0e2c4 From 7536cf4c7c80ea704402d9760a34150166849f6c Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 3 Dec 2023 11:41:06 +0100 Subject: [PATCH 023/166] implement value.hpp Signed-off-by: Folling --- src/values/number_value.hpp | 3 +- src/values/text_value.hpp | 2 +- src/values/toggle_value.hpp | 2 +- src/values/value.cpp | 152 +++++++++++++++++------------------- src/values/value.hpp | 6 +- src/values/value_base.hpp | 24 ++++-- 6 files changed, 93 insertions(+), 96 deletions(-) diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index e1b6915..359e5d2 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -20,5 +21,5 @@ public: ~IkarusNumberValue() override = default; public: - boost::container::vector value{}; + boost::variant2::variant> data{}; }; diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 9596a48..191206e 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -20,5 +20,5 @@ public: ~IkarusTextValue() override = default; public: - boost::container::vector value{}; + boost::variant2::variant> data{}; }; diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index cb8d110..750af25 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -20,5 +20,5 @@ public: ~IkarusToggleValue() override = default; public: - boost::container::vector value{}; + boost::variant2::variant> data{}; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index e0e9355..dd1b60e 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -2,9 +2,10 @@ #include -#include +#include +#include -#include +#include #include @@ -14,12 +15,11 @@ #include IkarusValue::IkarusValue(Data data): - _data(data) {} + data(data) {} cppbase::Result IkarusValue::from_json(boost::json::value const& json) { - bool const * intermediate = nullptr; boost::int64_t const * type = nullptr; - boost::json::array const * data = nullptr; + boost::json::value const * data = nullptr; if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); @@ -30,103 +30,91 @@ cppbase::Result IkarusValue::from_json( return cppbase::err(FromJsonError{}); } - if (auto const * intermediate_value = obj->if_contains("intermediate"); intermediate_value == nullptr) { - return cppbase::err(FromJsonError{}); - } else if (intermediate = intermediate_value->if_bool(); intermediate == nullptr) { + if (data = obj->if_contains("data"); data == nullptr) { return cppbase::err(FromJsonError{}); } - if (auto const * data = obj->if_contains("data"); data == nullptr) { - return cppbase::err(FromJsonError{}); - } else if (auto * array = data->if_array(); array == nullptr) { - return cppbase::err(FromJsonError{}); - } - - auto assign_value = - []( - auto const& value, boost::json::array const& array, auto convert = [](auto const& val) { return val; } - ) { - for (auto const& data : array) { - if (auto res = boost::json::try_value_to(data); res.has_error()) { - return cppbase::err(FromJsonError{}); - } else { - value->get_data().push_back(convert(res.value())); - return cppbase::ok(); - } - } - }; - switch (*type) { case IkarusPropertyType_Toggle: { auto * ret = new IkarusToggleValue{}; - ret->set_intermediate(*intermediate); + auto res = boost::json::try_value_to>(*data); - assign_value.operator()(ret, *data); + if (res.has_error()) { + return cppbase::err(FromJsonError{}); + } + + ret->data = std::move(res.value()); } case IkarusPropertyType_Number: { - auto * ret = new IkarusToggleValue{}; - ret->set_intermediate(*intermediate); + auto * ret = new IkarusNumberValue{}; + auto res = boost::json::try_value_to>(*data); - assign_value.operator()(ret, *data); + if (res.has_error()) { + return cppbase::err(FromJsonError{}); + } + + ret->data = std::move(res.value()); } case IkarusPropertyType_Text: { - auto * ret = new IkarusToggleValue{}; - ret->set_intermediate(*intermediate); + auto * ret = new IkarusTextValue{}; + auto res = boost::json::try_value_to>(*data); - assign_value.operator()(ret, *data); + if (res.has_error()) { + return cppbase::err(FromJsonError{}); + } + + ret->data = std::move(res.value()); + } + default: { + return cppbase::err(FromJsonError{}); } - default: return cppbase::err(FromJsonError{}); } } } boost::json::value IkarusValue::to_json() const { + auto type = boost::variant2::visit( + boost::make_overloaded_function( + []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusPropertyType_Toggle; }, + []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusPropertyType_Number; }, + []([[maybe_unused]] IkarusTextValue const * value) { return IkarusPropertyType_Text; } + ), + data + ); + + auto data_json = boost::variant2::visit([](auto const * value) { return boost::json::value_from(value->data); }, data); + return { - {"indeterminate", _indeterminate}, - {"type", - std::visit( - cppbase::overloaded{ - []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusPropertyType_Toggle; }, - []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusPropertyType_Number; }, - []([[maybe_unused]] IkarusTextValue const * value) { return IkarusPropertyType_Text; }, - }, _data - )}, - {"data", std::visit([](auto const * value) { return boost::json::value_from(value->get_data()); }, _data)} + {"type", type}, + {"data", data_json} }; } -bool IkarusValue::is_indeterminate() const { - return _indeterminate; -} - -void IkarusValue::set_indeterminate(bool value) { - _indeterminate = value; -} - -IkarusValue::Data& IkarusValue::get_data() { - return _data; -} - -IkarusValue::Data const& IkarusValue::get_data() const { - return _data; -} - bool ikarus_value_is_indeterminate(IkarusValue const * value) { - return value->is_indeterminate(); + return boost::variant2::visit( + [](auto const * value) { return boost::variant2::holds_alternative(value->data); }, + value->data + ); } void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { - value->set_intermediate(indeterminate); + if (indeterminate) { + return boost::variant2::visit([](auto const * value) { value->data = boost::variant2::monostate{}; }, value->data); + } + + return boost::variant2::visit( + [](auto const * value) { value->data = boost::remove_pointer_t::data_type{}; }, value->data + ); } char const * ikarus_value_to_string(IkarusValue const * value) { - auto const str = std::visit( - cppbase::overloaded{ - [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->get_data() ? "true" : "false"; }, - [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->get_value()); }, - [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->get_value()); }, - }, - value->get_data() + auto const str = boost::variant2::visit( + boost::make_overloaded_function( + [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->data ? "true" : "false"; }, + [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->data); }, + [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->data); } + ), + value->data ); return strdup(str.data()); @@ -139,13 +127,13 @@ void ikarus_value_visit( void (*text_visitor)(IkarusTextValue *, void *), void * data ) { - std::visit( - cppbase::overloaded{ + boost::variant2::visit( + boost::make_overloaded_function( [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); }, - }, - value->get_data() + [text_visitor, data](IkarusTextValue * text_value) { text_visitor(text_value, data); } + ), + value->data ); } @@ -156,12 +144,12 @@ void ikarus_value_visit_const( void (*text_visitor)(IkarusTextValue const *, void *), void * data ) { - std::visit( - cppbase::overloaded{ + boost::variant2::visit( + boost::make_overloaded_function( [toggle_visitor, data](IkarusToggleValue const * toggle_value) { toggle_visitor(toggle_value, data); }, [number_visitor, data](IkarusNumberValue const * number_value) { number_visitor(number_value, data); }, - [text_visitor, data](IkarusTextValue const * text_value) { text_visitor(text_value, data); }, - }, - value->get_data() + [text_visitor, data](IkarusTextValue const * text_value) { text_visitor(text_value, data); } + ), + value->data ); } diff --git a/src/values/value.hpp b/src/values/value.hpp index c58d9c7..3fbfad9 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -1,14 +1,13 @@ #pragma once -#include - #include +#include #include struct IkarusValue { public: - using Data = std::variant; + using Data = boost::variant2::variant; public: explicit IkarusValue(Data data); @@ -28,6 +27,5 @@ public: [[nodiscard]] boost::json::value to_json() const; public: - bool indeterminate{false}; Data data; }; diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp index 7fbb589..bad1b91 100644 --- a/src/values/value_base.hpp +++ b/src/values/value_base.hpp @@ -2,31 +2,41 @@ template typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { - if (value->is_indeterminate()) { - return nullptr; + if (auto * data = value->data.template get_if>(); data != nullptr) { + return &(*data)[idx]; } - return &value->value[idx]; + return nullptr; } template size_t ikarus_value_base_get_size(V const * value) { - return value->value.size(); + if (auto * data = value->data.template get_if>(); data != nullptr) { + return data->size(); + } + + return 0; } template void ikarus_value_base_set(V * value, size_t idx, typename V::data_type new_data) { - value->value[idx] = new_data; + if (auto * data = value->data.template get_if>(); data != nullptr) { + (*data)[idx] = new_data; + } } template void ikarus_value_base_remove(V * value, size_t idx) { - value->value.erase(value->value.begin() + idx); + if (auto * data = value->data.template get_if>(); data != nullptr) { + data->erase(data->begin() + idx); + } } template void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_data) { - value->value.insert(value->value.begin() + idx, new_data); + if (auto * data = value->data.template get_if>(); data != nullptr) { + data->insert(data->begin() + idx, new_data); + } } template From 03c18debecd62a9c3ab4090dea3ca47176feb625 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 3 Dec 2023 18:44:09 +0100 Subject: [PATCH 024/166] move sub-functions of value impls to concrete types Signed-off-by: Folling --- include/ikarus/objects/properties/property.h | 2 +- include/ikarus/values/number_value.h | 70 +++++++++++++---- include/ikarus/values/text_value.h | 80 +++++++++++++++----- include/ikarus/values/toggle_value.h | 51 ++++++++++++- include/ikarus/values/value.h | 21 +---- src/values/number_value.cpp | 35 +++++++++ src/values/number_value.hpp | 4 +- src/values/text_value.cpp | 35 +++++++++ src/values/text_value.hpp | 3 +- src/values/toggle_value.cpp | 41 ++++++++++ src/values/toggle_value.hpp | 3 +- src/values/value.cpp | 76 +++++-------------- src/values/value.hpp | 1 + src/values/value_base.hpp | 36 +++++++++ 14 files changed, 339 insertions(+), 119 deletions(-) diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index 7aa6a2a..c0f37fa 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -84,7 +84,7 @@ IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusPro /// \pre \li Must exist. /// \return The default value of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); +IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusProperty const * property); /// \brief Visits a property. Calling the appropriate function for the property's type. /// \param property The property to visit. diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index 2bdeb85..7049af8 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -14,31 +14,23 @@ IKARUS_BEGIN_HEADER /// \brief A numeric value. For example "Age" or "Height". struct IkarusNumberValue; -/// \brief Creates a number value from long doubles. -/// \param data The number data or null if you wish to create an empty value. -/// \details LibIkarus does not take ownership of this array. -/// \param data_size The size of the data array. +/// \brief Creates a number value. /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create(long double * data, size_t data_size); - -/// \brief Creates an indeterminate number value. -/// \return The value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); +IKA_API IkarusNumberValue * ikarus_number_value_create(); /// \brief Fetches the underlying data of a number value at a specific index. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. -/// \return The underlying data or null if an error occurs or the value is indeterminate. +/// \return The underlying data or null if an error occurs or the value is undefined. IKA_API long double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx); /// \brief Fetches the size of the underlying data of a number value. /// \param value The number value. /// \pre \li Must not be null. -/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +/// \return The size of the underlying data or 0 if an error occurs or the value is undefined. IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); /// \brief Sets the data of a number value at a specific index. @@ -47,14 +39,13 @@ IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); /// \param idx The index of the data to set. /// \pre \li Must be less than the size of the value. /// \param new_data The new data. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double new_data); +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double const * new_data); /// \brief Removes a data from a number value. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to remove. /// \pre \li Must be less than the size of the value. -/// \remark This will shift all data after the index by one to the left. IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); /// \brief Inserts a data into a number value. @@ -63,14 +54,61 @@ IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. /// \param new_data The new data. -/// \remark This will shift all data after the index by one to the right. -IKA_API void ikarus_number_insert(IkarusNumberValue * value, size_t idx, long double new_data); +IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, long double const * new_data); + +/// \brief Clears a number value. +/// \param value The number value. +/// \remark Noop if the value is undefined. +IKA_API void ikarus_number_value_clear(IkarusNumberValue * value); + +/// \brief Checks if a number value is undefined. +/// \param value The number value. +/// \pre \li Must not be null. +/// \return True if the value is undefined, false otherwise. +IKA_API bool ikarus_number_value_is_undefined(IkarusNumberValue const * value); + +/// \brief Changes a number value's undefined state. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param undefined The new undefined state. +/// \remark Noop if the value is already undefined. +/// \remark If the value is set to undefined, all data will be cleared. +/// \remark If the value is set to not undefined, the value is as if newly created. +IKA_API void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined); + +/// \brief Converts a number value to a string. +/// \param value The number value to convert. +/// \pre \li Must not be null. +/// \return The converted string. +/// \remark Must be freed with #ikarus_free. +/// \remark Undefined if the value is undefined. +IKA_API char const * ikarus_number_value_to_string(IkarusNumberValue const * value); + +/// \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. +/// \return True if the values' data are equal, false otherwise. +IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs); + +/// \brief Creates a copy of a number value. +/// \param value The value to copy. +/// \pre \li Must not be null. +/// \return The copied value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value); /// \brief Converts a number value to an entity value. /// \param value The number value to convert. +/// \pre \li Must not be null. /// \return The converted entity value. +/// \remark This is the same pointer, so freeing it implies freeing the original value. IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value); +/// \see ikarus_toggle_value_to_value +IKA_API struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index 08a37c8..b40a7f6 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -11,29 +11,27 @@ IKARUS_BEGIN_HEADER -/// \brief A textual value. For example "Surname" or "Description". +/// \brief A textual value. For example "Surname" or "Description" struct IkarusTextValue; -/// \brief Creates a text value from strings. -/// \param data The text data or null if you wish to create an empty value. -/// \details LibIkarus does not take ownership of this array. -/// \param data_size The size of the data array. +/// \brief Creates a text value. /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create(char const ** data, size_t data_size); +IKA_API IkarusTextValue * ikarus_text_value_create(); -/// \brief Creates an indeterminate text value. -/// \return The value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); - -/// \see ikarus_number_value_get -IKA_API char const * const * ikarus_text_value_get(IkarusTextValue const * value, size_t idx); +/// \brief Fetches the underlying data of a number value at a specific index. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param idx The index of the data to fetch. +/// \pre \li Must be less than the size of the value. +/// \return The underlying data or null if an error occurs or the value is undefined. +/// \remark This value is owned by LibIkarus and must not be freed. +IKA_API char const * ikarus_text_value_get(IkarusTextValue * value, size_t idx); /// \brief Fetches the size of the underlying data of a text value. /// \param value The text value. /// \pre \li Must not be null. -/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +/// \return The size of the underlying data or 0 if an error occurs or the value is undefined. IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value); /// \brief Sets the data of a text value at a specific index. @@ -41,7 +39,7 @@ IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * 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 new_data The new data. LibIkarus assumes ownership of this pointer. IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data); /// \brief Removes a data from a text value. @@ -49,7 +47,6 @@ IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char con /// \pre \li Must not be null. /// \param idx The index of the data to remove. /// \pre \li Must be less than the size of the value. -/// \remark This will shift all data after the index by one to the left. IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx); /// \brief Inserts a data into a text value. @@ -58,14 +55,61 @@ IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx); /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. /// \param new_data The new data. -/// \remark This will shift all data after the index by one to the right. -IKA_API void ikarus_text_insert(IkarusTextValue * value, size_t idx, char const * new_data); +IKA_API void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data); + +/// \brief Clears a text value. +/// \param value The text value. +/// \remark Noop if the value is undefined. +IKA_API void ikarus_text_value_clear(IkarusTextValue * value); + +/// \brief Checks if a text value is undefined. +/// \param value The text value. +/// \pre \li Must not be null. +/// \return True if the value is undefined, false otherwise. +IKA_API bool ikarus_text_value_is_undefined(IkarusTextValue const * value); + +/// \brief Changes a text value's undefined state. +/// \param value The text value. +/// \pre \li Must not be null. +/// \param undefined The new undefined state. +/// \remark Noop if the value is already undefined. +/// \remark If the value is set to undefined, all data will be cleared. +/// \remark If the value is set to not undefined, the value is as if newly created. +IKA_API void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined); + +/// \brief Converts a text value to a string. +/// \param value The text value to convert. +/// \pre \li Must not be null. +/// \return The converted string. +/// \remark Must be freed with #ikarus_free. +/// \remark Undefined if the value is undefined. +IKA_API char const * ikarus_text_value_to_string(IkarusTextValue const * value); + +/// \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. +/// \return True if the values' data are equal, false otherwise. +IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs); + +/// \brief Creates a copy of a text value. +/// \param value The value to copy. +/// \pre \li Must not be null. +/// \return The copied value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value); /// \brief Converts a text value to an entity value. /// \param value The text value to convert. +/// \pre \li Must not be null. /// \return The converted entity value. +/// \remark This is the same pointer, so freeing it implies freeing the original value. IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value); +/// \see ikarus_toggle_value_to_value +IKA_API struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index 2fe45d3..2bb84c7 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -14,7 +14,7 @@ IKARUS_BEGIN_HEADER /// \brief A true/false boolean-esque value. For example "Is Dead". struct IkarusToggleValue; -/// \brief Creates a toggle value from booleans. +/// \brief Creates a toggle. /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. IKA_API IkarusToggleValue * ikarus_toggle_value_create(); @@ -24,13 +24,13 @@ IKA_API IkarusToggleValue * ikarus_toggle_value_create(); /// \pre \li Must not be null. /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. -/// \return The underlying data or null if an error occurs or the value is indeterminate. +/// \return The underlying data or null if an error occurs or the value is undefined. IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue * value, size_t idx); /// \brief Fetches the size of the underlying data of a toggle value. /// \param value The toggle value. /// \pre \li Must not be null. -/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +/// \return The size of the underlying data or 0 if an error occurs or the value is undefined. IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value); /// \brief Sets the data of a toggle value at a specific index. @@ -58,14 +58,57 @@ IKA_API void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, b /// \brief Clears a toggle value. /// \param value The toggle value. -/// \remark Noop if the value is indeterminate. +/// \remark Noop if the value is undefined. IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value); +/// \brief Checks if a toggle value is undefined. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \return True if the value is undefined, false otherwise. +IKA_API bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value); + +/// \brief Changes a toggle value's undefined state. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \param undefined The new undefined state. +/// \remark Noop if the value is already undefined. +/// \remark If the value is set to undefined, all data will be cleared. +/// \remark If the value is set to not undefined, the value is as if newly created. +IKA_API void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined); + +/// \brief Converts a toggle value to a string. +/// \param value The toggle value to convert. +/// \pre \li Must not be null. +/// \return The converted string. +/// \remark Must be freed with #ikarus_free. +/// \remark Undefined if the value is undefined. +IKA_API char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value); + +/// \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. +/// \return True if the values' data are equal, false otherwise. +IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs); + +/// \brief Creates a copy of a toggle value. +/// \param value The value to copy. +/// \pre \li Must not be null. +/// \return The copied value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value); + /// \brief Converts a toggle value to an entity value. /// \param value The toggle value to convert. +/// \pre \li Must not be null. /// \return The converted entity value. +/// \remark This is the same pointer, so freeing it implies freeing the original value. IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value); +/// \see ikarus_toggle_value_to_value +IKA_API struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index b894701..d33f87a 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -9,7 +9,7 @@ /// \brief The values of properties. /// \details Each entity has a value for each property it is associated with. /// These value classes represent plain objects. They are not associated with any entity. -/// Each value may be indeterminate. \see IkarusProperty +/// Each value may be undefined. \see IkarusProperty /// Values are stored as lists. If a property is "singular" then its value is a list of size 1. /// Values are typed, with types existing for each of the corresponding property types. /// When setting values for a property the type must match the property type and the value must be valid under the property's @@ -21,25 +21,6 @@ IKARUS_BEGIN_HEADER /// \brief The common type for all values. struct IkarusValue; -/// \brief Checks if a value is indeterminate. -/// \param value The value. -/// \pre \li Must not be null. -/// \return True if the value is indeterminate, false otherwise. -IKA_API bool ikarus_value_is_indeterminate(IkarusValue const * value); - -/// \brief Sets the indeterminate state of a value. -/// \param value The value. -/// \pre \li Must not be null. -/// \param indeterminate The new indeterminate state. -IKA_API void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate); - -/// \brief Converts an entity value to a string. -/// \pre \li Must not be null. -/// \param value The entity value. -/// \return A string representation of the value or null if an error occurred. -/// \remark The returned value is owned by the caller. -IKA_API char const * ikarus_value_to_string(IkarusValue const * value); - /// \brief Visits an entity value, calling the appropriate function for the value's type. /// \param value The entity value to visit. /// \pre \li Must not be null. diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp index c928b10..cef3508 100644 --- a/src/values/number_value.cpp +++ b/src/values/number_value.cpp @@ -1,5 +1,7 @@ #include "ikarus/values/number_value.h" +#include + #include #include @@ -30,6 +32,39 @@ void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, long doub return ikarus_value_base_insert(value, idx, new_data); } +void ikarus_number_value_clear(IkarusNumberValue * value) { + return ikarus_value_base_clear(value); +} + +bool ikarus_number_value_is_undefined(IkarusNumberValue const * value) { + return ikarus_value_base_is_undefined(value); +} + +void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined) { + return ikarus_value_base_set_undefined(value, undefined); +} + +char const * ikarus_number_value_to_string(IkarusNumberValue const * value) { + return boost::variant2::visit( + boost::make_overloaded_function( + [](boost::variant2::monostate const&) { return nullptr; }, [](auto const& data) { return fmt::join(data, ", "); } + ), + value->data + ); +} + +bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs) { + return ikarus_value_base_is_equal(lhs, rhs); +} + +IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value) { + return ikarus_value_base_copy(value); +} + struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value) { return ikarus_value_base_to_value(value); } + +struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value) { + return ikarus_value_base_to_value_const(value); +} diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index 359e5d2..6cad130 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -21,5 +21,7 @@ public: ~IkarusNumberValue() override = default; public: - boost::variant2::variant> data{}; + boost::variant2:: + variant> + data{}; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp index a382c0a..08ad03a 100644 --- a/src/values/text_value.cpp +++ b/src/values/text_value.cpp @@ -1,5 +1,7 @@ #include "ikarus/values/text_value.h" +#include + #include #include @@ -30,6 +32,39 @@ void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * return ikarus_value_base_insert(value, idx, new_data); } +void ikarus_text_value_clear(IkarusTextValue * value) { + return ikarus_value_base_clear(value); +} + +bool ikarus_text_value_is_undefined(IkarusTextValue const * value) { + return ikarus_value_base_is_undefined(value); +} + +void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined) { + return ikarus_value_base_set_undefined(value, undefined); +} + +char const * ikarus_text_value_to_string(IkarusTextValue const * value) { + return boost::variant2::visit( + boost::make_overloaded_function( + [](boost::variant2::monostate const&) { return nullptr; }, [](auto const& data) { return fmt::join(data, ", "); } + ), + value->data + ); +} + +bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs) { + return ikarus_value_base_is_equal(lhs, rhs); +} + +IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value) { + return ikarus_value_base_copy(value); +} + struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value) { return ikarus_value_base_to_value(value); } + +struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value) { + return ikarus_value_base_to_value_const(value); +} diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 191206e..8d063f2 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -20,5 +20,6 @@ public: ~IkarusTextValue() override = default; public: - boost::variant2::variant> data{}; + boost::variant2::variant> data{ + }; }; diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp index 8d11e5a..37b2069 100644 --- a/src/values/toggle_value.cpp +++ b/src/values/toggle_value.cpp @@ -1,5 +1,8 @@ #include "ikarus/values/toggle_value.h" +#include +#include + #include #include @@ -30,6 +33,44 @@ void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_ return ikarus_value_base_insert(value, idx, new_data); } +void ikarus_toggle_value_clear(IkarusToggleValue * value) { + return ikarus_value_base_clear(value); +} + +bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value) { + return ikarus_value_base_is_undefined(value); +} + +void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined) { + return ikarus_value_base_set_undefined(value, undefined); +} + +char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value) { + return boost::variant2::visit( + boost::make_overloaded_function( + [](boost::variant2::monostate const&) { return nullptr; }, + [](auto const& data) { + return fmt::join( + data | boost::adaptors::transformed([](auto const& bool_value) { return bool_value ? "✓" : "✗"; }), ", " + ); + } + ), + value->data + ); +} + +bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs) { + return ikarus_value_base_is_equal(lhs, rhs); +} + +IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value) { + return ikarus_value_base_copy(value); +} + struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value) { return ikarus_value_base_to_value(value); } + +struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value) { + return ikarus_value_base_to_value_const(value); +} diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index 750af25..6506dda 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -20,5 +20,6 @@ public: ~IkarusToggleValue() override = default; public: - boost::variant2::variant> data{}; + boost::variant2::variant> data{ + }; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index dd1b60e..b9a53a7 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -18,12 +18,12 @@ IkarusValue::IkarusValue(Data data): data(data) {} cppbase::Result IkarusValue::from_json(boost::json::value const& json) { - boost::int64_t const * type = nullptr; - boost::json::value const * data = nullptr; - if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); } else { + boost::int64_t const * type = nullptr; + boost::json::value const * data = nullptr; + if (auto const * type_value = obj->if_contains("type"); type_value == nullptr) { return cppbase::err(FromJsonError{}); } else if (type = type_value->if_int64(); type == nullptr) { @@ -34,36 +34,28 @@ cppbase::Result IkarusValue::from_json( return cppbase::err(FromJsonError{}); } + auto create_value = [data]() -> cppbase::Result { + auto * ret = new T{}; + auto res = boost::json::try_value_to>(*data); + + if (res.has_error()) { + return cppbase::err(FromJsonError{}); + } + + ret->data = std::move(res.value()); + + return cppbase::ok(ret); + }; + switch (*type) { case IkarusPropertyType_Toggle: { - auto * ret = new IkarusToggleValue{}; - auto res = boost::json::try_value_to>(*data); - - if (res.has_error()) { - return cppbase::err(FromJsonError{}); - } - - ret->data = std::move(res.value()); + return create_value.operator()(); } case IkarusPropertyType_Number: { - auto * ret = new IkarusNumberValue{}; - auto res = boost::json::try_value_to>(*data); - - if (res.has_error()) { - return cppbase::err(FromJsonError{}); - } - - ret->data = std::move(res.value()); + return create_value.operator()(); } case IkarusPropertyType_Text: { - auto * ret = new IkarusTextValue{}; - auto res = boost::json::try_value_to>(*data); - - if (res.has_error()) { - return cppbase::err(FromJsonError{}); - } - - ret->data = std::move(res.value()); + return create_value.operator()(); } default: { return cppbase::err(FromJsonError{}); @@ -90,36 +82,6 @@ boost::json::value IkarusValue::to_json() const { }; } -bool ikarus_value_is_indeterminate(IkarusValue const * value) { - return boost::variant2::visit( - [](auto const * value) { return boost::variant2::holds_alternative(value->data); }, - value->data - ); -} - -void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { - if (indeterminate) { - return boost::variant2::visit([](auto const * value) { value->data = boost::variant2::monostate{}; }, value->data); - } - - return boost::variant2::visit( - [](auto const * value) { value->data = boost::remove_pointer_t::data_type{}; }, value->data - ); -} - -char const * ikarus_value_to_string(IkarusValue const * value) { - auto const str = boost::variant2::visit( - boost::make_overloaded_function( - [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->data ? "true" : "false"; }, - [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->data); }, - [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->data); } - ), - value->data - ); - - return strdup(str.data()); -} - void ikarus_value_visit( IkarusValue * value, void (*toggle_visitor)(IkarusToggleValue *, void *), diff --git a/src/values/value.hpp b/src/values/value.hpp index 3fbfad9..14c2a73 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -8,6 +8,7 @@ struct IkarusValue { public: using Data = boost::variant2::variant; + constexpr auto SMALL_VEC_VALUE_SIZE = 8; public: explicit IkarusValue(Data data); diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp index bad1b91..04bad48 100644 --- a/src/values/value_base.hpp +++ b/src/values/value_base.hpp @@ -39,7 +39,43 @@ void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_d } } +template +void ikarus_value_base_clear(V * value) { + if (auto * data = value->data.template get_if>(); data != nullptr) { + data->clear(); + } +} + +template +bool ikarus_value_base_is_undefined(V const * value) { + return boost::variant2::holds_alternative(value->data); +} + +template +void ikarus_value_base_set_undefined(V * value, bool undefined) { + if (undefined) { + value->data = boost::variant2::monostate{}; + } else { + value->data = typename V::data_type{}; + } +} + +template +bool ikarus_value_base_is_equal(V const * lhs, V const * rhs) { + return lhs->data == rhs->data; +} + +template +V * ikarus_value_base_copy(V const * value) { + return new V{*value}; +} + template struct IkarusValue * ikarus_value_base_to_value(V * value) { return static_cast(value); } + +template +struct IkarusValue const * ikarus_value_base_to_value_const(V const * value) { + return static_cast(value); +} From 28230a162607caa48c1dae6a09c3012a8cdb3dd2 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 9 Dec 2023 14:02:05 +0100 Subject: [PATCH 025/166] fixup compiler errors & finalize json (de-)serialization for values Signed-off-by: Folling --- include/ikarus/objects/entity.h | 2 +- include/ikarus/values/number_value.h | 6 +-- src/objects/properties/property.cpp | 8 ++-- src/redirect/json.hpp | 3 ++ src/values/number_value.cpp | 11 ++--- src/values/number_value.hpp | 9 ++-- src/values/text_value.cpp | 8 +--- src/values/text_value.hpp | 2 +- src/values/toggle_value.cpp | 12 +---- src/values/toggle_value.hpp | 2 +- src/values/value.cpp | 40 ++++++++++++----- src/values/value.hpp | 4 +- src/values/value_base.hpp | 67 +++++++++++++++++++++++++--- 13 files changed, 116 insertions(+), 58 deletions(-) create mode 100644 src/redirect/json.hpp diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 2ac9087..5ce2266 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -112,7 +112,7 @@ IKA_API void ikarus_entity_get_properties( ); /// \brief Gets the value of a property of an entity. -/// \details If the entity has never set the value of the property, the default value is returned (which may be indeterminate). +/// \details If the entity has never set the value of the property, the default value is returned (which may be undefined). /// \param entity The entity to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index 7049af8..cedd61d 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -25,7 +25,7 @@ IKA_API IkarusNumberValue * ikarus_number_value_create(); /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. /// \return The underlying data or null if an error occurs or the value is undefined. -IKA_API long double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx); +IKA_API double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx); /// \brief Fetches the size of the underlying data of a number value. /// \param value The number value. @@ -39,7 +39,7 @@ IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); /// \param idx The index of the data to set. /// \pre \li Must be less than the size of the value. /// \param new_data The new data. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double const * new_data); +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double const * new_data); /// \brief Removes a data from a number value. /// \param value The number value. @@ -54,7 +54,7 @@ IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. /// \param new_data The new data. -IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, long double const * new_data); +IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, double const * new_data); /// \brief Clears a number value. /// \param value The number value. diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index d1eb94a..00ab888 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -91,8 +91,9 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p auto * ctx = property->get_project()->get_function_context(); - VTRY( + VTRYRV( auto const source, + nullptr, property->get_project() ->get_db() ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->get_id()) @@ -129,8 +130,9 @@ IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) auto * ctx = property->get_project()->get_function_context(); - VTRY( + VTRYRV( auto const value, + nullptr, property->get_project() ->get_db() ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->get_id()) @@ -144,7 +146,7 @@ IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) }) ); - return new IkarusValue(property->get_project(), value); + return IkarusValue::from_json(value).unwrap_value_or(nullptr); } void ikarus_property_visit( diff --git a/src/redirect/json.hpp b/src/redirect/json.hpp new file mode 100644 index 0000000..c45bde6 --- /dev/null +++ b/src/redirect/json.hpp @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp index cef3508..b408925 100644 --- a/src/values/number_value.cpp +++ b/src/values/number_value.cpp @@ -12,7 +12,7 @@ IkarusNumberValue * ikarus_number_value_create() { return new IkarusNumberValue{}; } -long double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx) { +double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx) { return ikarus_value_base_get(value, idx); } @@ -20,7 +20,7 @@ size_t ikarus_number_value_get_size(IkarusNumberValue const * value) { return ikarus_value_base_get_size(value); } -void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double new_data) { +void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double new_data) { return ikarus_value_base_set(value, idx, new_data); } @@ -45,12 +45,7 @@ void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined } char const * ikarus_number_value_to_string(IkarusNumberValue const * value) { - return boost::variant2::visit( - boost::make_overloaded_function( - [](boost::variant2::monostate const&) { return nullptr; }, [](auto const& data) { return fmt::join(data, ", "); } - ), - value->data - ); + return ikarus_value_base_to_string(value, [](auto const& value) { return value; }); } bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs) { diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index 6cad130..16f99c7 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -1,13 +1,13 @@ #pragma once -#include +#include #include #include struct IkarusNumberValue final : IkarusValue { public: - using data_type = long double; + using data_type = double; public: explicit IkarusNumberValue(); @@ -21,7 +21,6 @@ public: ~IkarusNumberValue() override = default; public: - boost::variant2:: - variant> - data{}; + boost::variant2::variant> data{ + }; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp index 08ad03a..ff09770 100644 --- a/src/values/text_value.cpp +++ b/src/values/text_value.cpp @@ -1,5 +1,6 @@ #include "ikarus/values/text_value.h" +#include #include #include @@ -45,12 +46,7 @@ void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined) { } char const * ikarus_text_value_to_string(IkarusTextValue const * value) { - return boost::variant2::visit( - boost::make_overloaded_function( - [](boost::variant2::monostate const&) { return nullptr; }, [](auto const& data) { return fmt::join(data, ", "); } - ), - value->data - ); + return ikarus_value_base_to_string(value, [](auto const& value) { return value; }); } bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs) { diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 8d063f2..1ff74ac 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp index 37b2069..c5ced27 100644 --- a/src/values/toggle_value.cpp +++ b/src/values/toggle_value.cpp @@ -46,17 +46,7 @@ void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined } char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value) { - return boost::variant2::visit( - boost::make_overloaded_function( - [](boost::variant2::monostate const&) { return nullptr; }, - [](auto const& data) { - return fmt::join( - data | boost::adaptors::transformed([](auto const& bool_value) { return bool_value ? "✓" : "✗"; }), ", " - ); - } - ), - value->data - ); + return ikarus_value_base_to_string(value, [](auto const& value) { return value ? "✓" : "✗"; }); } bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs) { diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index 6506dda..20c579c 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/src/values/value.cpp b/src/values/value.cpp index b9a53a7..81cdaa9 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -2,10 +2,11 @@ #include +#include #include -#include -#include +// required for header-only inclusion +#include #include @@ -17,7 +18,7 @@ IkarusValue::IkarusValue(Data data): data(data) {} -cppbase::Result IkarusValue::from_json(boost::json::value const& json) { +cppbase::Result IkarusValue::from_json(boost::json::value const& json) { if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); } else { @@ -35,15 +36,23 @@ cppbase::Result IkarusValue::from_json( } auto create_value = [data]() -> cppbase::Result { - auto * ret = new T{}; - auto res = boost::json::try_value_to>(*data); + T * ret = nullptr; - if (res.has_error()) { - return cppbase::err(FromJsonError{}); + if (data->is_null()) { + ret = new T{}; + ret->data = boost::variant2::monostate{}; + } else { + auto res = boost::json::try_value_to< + boost::container::small_vector>(*data); + + if (res.has_error()) { + return cppbase::err(FromJsonError{}); + } + + ret = new T{}; + ret->data = std::move(res.value()); } - ret->data = std::move(res.value()); - return cppbase::ok(ret); }; @@ -74,7 +83,18 @@ boost::json::value IkarusValue::to_json() const { data ); - auto data_json = boost::variant2::visit([](auto const * value) { return boost::json::value_from(value->data); }, data); + auto data_json = boost::variant2::visit( + [](T const * value) -> boost::json::value { + return boost::variant2::visit( + boost::make_overloaded_function( + []([[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 { {"type", type}, diff --git a/src/values/value.hpp b/src/values/value.hpp index 14c2a73..20b4717 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -8,7 +8,7 @@ struct IkarusValue { public: using Data = boost::variant2::variant; - constexpr auto SMALL_VEC_VALUE_SIZE = 8; + constexpr static auto SMALL_VEC_VALUE_SIZE = 8; public: explicit IkarusValue(Data data); @@ -24,7 +24,7 @@ public: public: struct FromJsonError {}; - [[nodiscard]] static cppbase::Result from_json(boost::json::value const& json); + [[nodiscard]] static cppbase::Result from_json(boost::json::value const& json); [[nodiscard]] boost::json::value to_json() const; public: diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp index 04bad48..416be34 100644 --- a/src/values/value_base.hpp +++ b/src/values/value_base.hpp @@ -1,8 +1,19 @@ #pragma once +#include +#include + +#include +#include +#include + template typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { return &(*data)[idx]; } @@ -11,7 +22,11 @@ typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { template size_t ikarus_value_base_get_size(V const * value) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { return data->size(); } @@ -20,28 +35,44 @@ size_t ikarus_value_base_get_size(V const * value) { template void ikarus_value_base_set(V * value, size_t idx, typename V::data_type new_data) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { (*data)[idx] = new_data; } } template void ikarus_value_base_remove(V * value, size_t idx) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { data->erase(data->begin() + idx); } } template void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_data) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { data->insert(data->begin() + idx, new_data); } } template void ikarus_value_base_clear(V * value) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { data->clear(); } } @@ -56,10 +87,32 @@ void ikarus_value_base_set_undefined(V * value, bool undefined) { if (undefined) { value->data = boost::variant2::monostate{}; } else { - value->data = typename V::data_type{}; + value->data = boost::container::small_vector{}; } } +template F> +char const * ikarus_value_base_to_string(V const * value, F transformer) { + return boost::variant2::visit( + boost::make_overloaded_function( + [](boost::variant2::monostate const&) -> char const * { return nullptr; }, + [&transformer](boost::container::small_vector const& data + ) -> char const * { + auto buffer = fmt::memory_buffer{}; + + fmt::format_to( + std::back_inserter(buffer), + "{}", + fmt::join(data | std::views::transform(std::forward(transformer)), ", ") + ); + + return buffer.data(); + } + ), + value->data + ); +} + template bool ikarus_value_base_is_equal(V const * lhs, V const * rhs) { return lhs->data == rhs->data; From 1d155280e3da099b38fb55224e011e0ac12d853f Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 10 Dec 2023 02:15:29 +0100 Subject: [PATCH 026/166] remove property settings Signed-off-by: Folling --- .../settings/number_property_settings.h | 31 ------- .../properties/settings/property_settings.h | 93 ------------------- .../settings/text_property_settings.h | 31 ------- .../settings/toggle_property_settings.h | 31 ------- 4 files changed, 186 deletions(-) delete mode 100644 include/ikarus/objects/properties/settings/number_property_settings.h delete mode 100644 include/ikarus/objects/properties/settings/property_settings.h delete mode 100644 include/ikarus/objects/properties/settings/text_property_settings.h delete mode 100644 include/ikarus/objects/properties/settings/toggle_property_settings.h diff --git a/include/ikarus/objects/properties/settings/number_property_settings.h b/include/ikarus/objects/properties/settings/number_property_settings.h deleted file mode 100644 index fcdaee8..0000000 --- a/include/ikarus/objects/properties/settings/number_property_settings.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file number_property_settings.h -/// \author Folling - -#include - -/// \addtogroup property_settings PropertySettings -/// \brief Number property settings add additional constraints to number properties. -/// \details The following settings are available: -/// -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusNumberPropertySettings; - -/// \brief Sets the default value for a number property. -/// \param settings The number property settings. -/// \pre \li Must not be null. -/// \param default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \remark The settings take ownership of the value, the caller must not free it. -IKA_API void ikarus_number_property_settings_set_default_value( - struct IkarusNumberPropertySettings * settings, struct IkarusNumberValue * default_value -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/settings/property_settings.h b/include/ikarus/objects/properties/settings/property_settings.h deleted file mode 100644 index a47541e..0000000 --- a/include/ikarus/objects/properties/settings/property_settings.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -/// \file property_settings.h -/// \author Folling - -#include - -/// \defgroup property_settings PropertySettings -/// \brief Property settings add additional constraints to properties. -/// \details Each property has a certain set of settings. The options available depend on the type of the property. -/// Settings can be changed after the property has been created. Examples of settings are: -/// Note that the default value must be set using the concrete subtypes to ascertain type correctness. -/// - Minimum: The minimum value for a number property. -/// - Matches: A regular expression that the value of a text property must match. -/// There are also some common settings, shared among all properties: -/// - Default Value (default: PropertyType's default default (sic) value): The value that is returned if no value is specified -/// for some entity. -/// - List (default: false): If set to true, the property becomes a list. Instead of one number, you could then specify a series -/// of numbers. Note that each element in the list is subject to changes in values. E.g. when we say that all values are changed -/// in some way (e.g. reset to the default value), this applies to all elements in the list. -/// - Allow undefined (default: false): If set to true, you can specify an "unknown" value for a property. -/// It might not be known if a character is dead or not for example. -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusPropertySettings; - -/// \brief Gets the default value of a property. -/// \param settings The settings to get the default value of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The default value of the property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue const * ikarus_property_settings_get_default_value(IkarusPropertySettings const * settings); - -/// \brief Fetches whether a property is a list. -/// \param settings The property settings. -/// \pre \li Must not be null. -/// \return True if the property is a list, false otherwise. -IKA_API bool ikarus_property_settings_is_list(IkarusPropertySettings const * settings); - -/// \brief Sets whether a property is a list. -/// \details Noop if unchanged. A change from list -> single truncates to just the first element. A change from single -> list -/// sets the first element to the current value. -/// \param settings The property settings. -/// \pre \li Must not be null. -/// \param list Whether the property should be -/// a list. -IKA_API void ikarus_property_settings_set_is_list(IkarusPropertySettings * settings, bool list); - -/// \brief Fetches whether a property may be undefined. -/// \param settings The property settings. -/// \pre \li Must not be null. -/// \return True if the property may be undefined, false otherwise. -IKA_API bool ikarus_property_settings_may_be_undefined(IkarusPropertySettings const * settings); - -/// \brief Sets whether a property may be undefined. -/// \details Noop if unchanged. If the transition is from undefined -> defined, all undefined values will be reset to the -/// default value. -/// \param settings The property settings. -/// \pre \li Must not be null. -/// \param allow_undefined Whether the property should be allowed to be undefined. -/// \param allow_undefined Whether the property should be allowed to be undefined. -IKA_API void ikarus_property_settings_set_may_be_undefined(IkarusPropertySettings * settings, bool allow_undefined); - -/// \brief Visits a property settings. Calling the appropriate function for the property's type. -/// \param settings The property settings. -/// \pre \li Must not be null. -/// \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 Data passed to the visitors. -IKA_API void ikarus_property_settings_visit( - struct IkarusPropertySettings * settings, - void (*toggle_property_visitor)(struct IkarusTogglePropertySettings * settings, void * data), - void (*number_property_visitor)(struct IkarusNumberPropertySettings * settings, void * data), - void (*text_property_visitor)(struct IkarusTextPropertySettings * settings, void * data), - void * data -); - -/// \see ikarus_property_settings_visit -IKA_API void ikarus_property_settings_visit_const( - struct IkarusPropertySettings const * settings, - void (*toggle_property_visitor)(struct IkarusTogglePropertySettings const * settings, void * data), - void (*number_property_visitor)(struct IkarusNumberPropertySettings const * settings, void * data), - void (*text_property_visitor)(struct IkarusTextPropertySettings const * settings, void * data), - void * data -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/settings/text_property_settings.h b/include/ikarus/objects/properties/settings/text_property_settings.h deleted file mode 100644 index 926058d..0000000 --- a/include/ikarus/objects/properties/settings/text_property_settings.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file text_property_settings.h -/// \author Folling - -#include - -/// \addtogroup property_settings PropertySettings -/// \brief Text property settings add additional constraints to text properties. -/// \details The following settings are available: -/// -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusTextPropertySettings; - -/// \brief Sets the default value for a text property. -/// \param settings The text property settings. -/// \pre \li Must not be null. -/// \param default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \remark The settings take ownership of the value, the caller must not free it. -IKA_API void ikarus_text_property_settings_set_default_value( - struct IkarusTextPropertySettings * settings, struct IkarusTextValue * default_value -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/settings/toggle_property_settings.h b/include/ikarus/objects/properties/settings/toggle_property_settings.h deleted file mode 100644 index d372929..0000000 --- a/include/ikarus/objects/properties/settings/toggle_property_settings.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file toggle_property_settings.h -/// \author Folling - -#include - -/// \addtogroup property_settings PropertySettings -/// \brief Toggle property settings add additional constraints to toggle properties. -/// \details The following settings are available: -/// -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusTogglePropertySettings; - -/// \brief Sets the default value for a toggle property. -/// \param settings The toggle property settings. -/// \pre \li Must not be null. -/// \param default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \remark The settings take ownership of the value, the caller must not free it. -IKA_API void ikarus_toggle_property_settings_set_default_value( - struct IkarusTogglePropertySettings * settings, struct IkarusToggleValue * default_value -); - -IKARUS_END_HEADER - -/// @} From 88a9a04dcd696f67d7467f67f04566a6a0e41a4a Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 10 Dec 2023 02:16:30 +0100 Subject: [PATCH 027/166] remove json redirect helper Signed-off-by: Folling --- src/redirect/json.hpp | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/redirect/json.hpp diff --git a/src/redirect/json.hpp b/src/redirect/json.hpp deleted file mode 100644 index c45bde6..0000000 --- a/src/redirect/json.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include From 8a765739835ababb9da307d7fc2e78f6ce04aefc Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 11 Dec 2023 16:16:34 +0100 Subject: [PATCH 028/166] implement IkarusPropertySource and switch to cppbase::overloaded Signed-off-by: Folling --- src/objects/blueprint.hpp | 2 +- src/objects/entity.hpp | 2 +- src/objects/properties/property_source.cpp | 46 ++++++++++++++++++++++ src/objects/properties/property_source.hpp | 16 +++----- src/objects/properties/toggle_property.hpp | 2 +- src/values/number_value.hpp | 6 +-- src/values/text_value.hpp | 6 +-- src/values/toggle_value.hpp | 6 +-- src/values/value.cpp | 21 +++++----- src/values/value_base.hpp | 33 ++++++++-------- 10 files changed, 90 insertions(+), 50 deletions(-) diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index 41042ba..6fa6281 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -2,7 +2,7 @@ #include -struct IkarusBlueprint final : IkarusObject { +struct IkarusBlueprint : IkarusObject { IkarusBlueprint(struct IkarusProject * project, IkarusId id); IkarusBlueprint(IkarusBlueprint const&) = default; diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index fb78e39..f6d4f90 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -2,7 +2,7 @@ #include -struct IkarusEntity final : IkarusObject { +struct IkarusEntity : IkarusObject { inline IkarusEntity(struct IkarusProject * project, IkarusId id): IkarusObject{project, id} {} diff --git a/src/objects/properties/property_source.cpp b/src/objects/properties/property_source.cpp index e69de29..364ca84 100644 --- a/src/objects/properties/property_source.cpp +++ b/src/objects/properties/property_source.cpp @@ -0,0 +1,46 @@ +#include "property_source.hpp" + +#include + +#include + +IkarusPropertySource::IkarusPropertySource(Data data): + data{data} {} + +IkarusPropertySource * ikarus_property_source_create_blueprint(IkarusBlueprint * blueprint) { + return new IkarusPropertySource{blueprint}; +} + +IkarusPropertySource * ikarus_property_source_create_entity(IkarusEntity * entity) { + return new IkarusPropertySource{entity}; +} + +void ikarus_property_source_visit( + struct IkarusPropertySource * property_source, + void (*blueprint_visitor)(struct IkarusBlueprint *, void *), + void (*entity_visitor)(struct IkarusEntity *, void *), + void * user_data +) { + boost::variant2::visit( + boost::make_overloaded_function( + [blueprint_visitor, user_data](IkarusBlueprint * blueprint) { blueprint_visitor(blueprint, user_data); }, + [entity_visitor, user_data](IkarusEntity * entity) { entity_visitor(entity, user_data); } + ), + property_source->data + ); +} + +void ikarus_property_source_visit_const( + struct IkarusPropertySource const * property_source, + void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), + void (*entity_visitor)(struct IkarusEntity const *, void *), + void * user_data +) { + boost::variant2::visit( + boost::make_overloaded_function( + [blueprint_visitor, user_data](IkarusBlueprint const * blueprint) { blueprint_visitor(blueprint, user_data); }, + [entity_visitor, user_data](IkarusEntity const * entity) { entity_visitor(entity, user_data); } + ), + property_source->data + ); +} diff --git a/src/objects/properties/property_source.hpp b/src/objects/properties/property_source.hpp index 8da517a..0fda4ed 100644 --- a/src/objects/properties/property_source.hpp +++ b/src/objects/properties/property_source.hpp @@ -1,16 +1,15 @@ #pragma once -#include +#include #include struct IkarusPropertySource { public: - using Data = std::variant; + using Data = boost::variant2::variant; public: - inline explicit IkarusPropertySource(Data data): - _data{data} {} + explicit IkarusPropertySource(Data data); IkarusPropertySource(IkarusPropertySource const&) = default; IkarusPropertySource(IkarusPropertySource&&) = default; @@ -18,13 +17,8 @@ public: IkarusPropertySource& operator=(IkarusPropertySource const&) = default; IkarusPropertySource& operator=(IkarusPropertySource&&) = default; - ~IkarusPropertySource() = default; + virtual ~IkarusPropertySource() = default; public: - [[nodiscard]] inline Data const& get_data() const { - return _data; - } - -private: - std::variant _data; + Data data; }; diff --git a/src/objects/properties/toggle_property.hpp b/src/objects/properties/toggle_property.hpp index 2ca2520..92bbbc4 100644 --- a/src/objects/properties/toggle_property.hpp +++ b/src/objects/properties/toggle_property.hpp @@ -2,7 +2,7 @@ #include -struct IkarusToggleProperty final : IkarusProperty { +struct IkarusToggleProperty : IkarusProperty { public: IkarusToggleProperty(struct IkarusProject * project, IkarusId id); }; diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index 16f99c7..1421ca3 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -5,9 +5,9 @@ #include -struct IkarusNumberValue final : IkarusValue { +struct IkarusNumberValue : IkarusValue { public: - using data_type = double; + using DataType = double; public: explicit IkarusNumberValue(); @@ -21,6 +21,6 @@ public: ~IkarusNumberValue() override = default; public: - boost::variant2::variant> data{ + boost::variant2::variant> data{ }; }; diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 1ff74ac..117e175 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -4,9 +4,9 @@ #include -struct IkarusTextValue final : IkarusValue { +struct IkarusTextValue : IkarusValue { public: - using data_type = std::string; + using DataType = std::string; public: explicit IkarusTextValue(); @@ -20,6 +20,6 @@ public: ~IkarusTextValue() override = default; public: - boost::variant2::variant> data{ + boost::variant2::variant> data{ }; }; diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index 20c579c..7e2240f 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -4,9 +4,9 @@ #include -struct IkarusToggleValue final : IkarusValue { +struct IkarusToggleValue : IkarusValue { public: - using data_type = bool; + using DataType = bool; public: explicit IkarusToggleValue(); @@ -20,6 +20,6 @@ public: ~IkarusToggleValue() override = default; public: - boost::variant2::variant> data{ + boost::variant2::variant> data{ }; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index 81cdaa9..2cffcaa 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -3,9 +3,10 @@ #include #include -#include // required for header-only inclusion +#include "cppbase/templates.hpp" + #include #include @@ -43,7 +44,7 @@ cppbase::Result IkarusValue::from_jso ret->data = boost::variant2::monostate{}; } else { auto res = boost::json::try_value_to< - boost::container::small_vector>(*data); + boost::container::small_vector>(*data); if (res.has_error()) { return cppbase::err(FromJsonError{}); @@ -75,21 +76,21 @@ cppbase::Result IkarusValue::from_jso boost::json::value IkarusValue::to_json() const { auto type = boost::variant2::visit( - boost::make_overloaded_function( + cppbase::overloaded{ []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusPropertyType_Toggle; }, []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusPropertyType_Number; }, []([[maybe_unused]] IkarusTextValue const * value) { return IkarusPropertyType_Text; } - ), + }, data ); auto data_json = boost::variant2::visit( [](T const * value) -> boost::json::value { return boost::variant2::visit( - boost::make_overloaded_function( + 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 ); }, @@ -110,11 +111,11 @@ void ikarus_value_visit( void * data ) { boost::variant2::visit( - boost::make_overloaded_function( + cppbase::overloaded{ [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); } - ), + }, value->data ); } @@ -127,11 +128,11 @@ void ikarus_value_visit_const( void * data ) { boost::variant2::visit( - boost::make_overloaded_function( + cppbase::overloaded{ [toggle_visitor, data](IkarusToggleValue const * toggle_value) { toggle_visitor(toggle_value, data); }, [number_visitor, data](IkarusNumberValue const * number_value) { number_visitor(number_value, data); }, [text_visitor, data](IkarusTextValue const * text_value) { text_visitor(text_value, data); } - ), + }, value->data ); } diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp index 416be34..46cf457 100644 --- a/src/values/value_base.hpp +++ b/src/values/value_base.hpp @@ -1,16 +1,15 @@ #pragma once -#include #include -#include +#include + #include -#include template -typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { +typename V::DataType const * ikarus_value_base_get(V * value, size_t idx) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -23,7 +22,7 @@ typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { template size_t ikarus_value_base_get_size(V const * value) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -34,9 +33,9 @@ size_t ikarus_value_base_get_size(V const * value) { } template -void ikarus_value_base_set(V * value, size_t idx, typename V::data_type new_data) { +void ikarus_value_base_set(V * value, size_t idx, typename V::DataType new_data) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -47,7 +46,7 @@ void ikarus_value_base_set(V * value, size_t idx, typename V::data_type new_data template void ikarus_value_base_remove(V * value, size_t idx) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -56,9 +55,9 @@ void ikarus_value_base_remove(V * value, size_t idx) { } template -void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_data) { +void ikarus_value_base_insert(V * value, size_t idx, typename V::DataType new_data) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -69,7 +68,7 @@ void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_d template void ikarus_value_base_clear(V * value) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -87,16 +86,16 @@ void ikarus_value_base_set_undefined(V * value, bool undefined) { if (undefined) { value->data = boost::variant2::monostate{}; } else { - value->data = boost::container::small_vector{}; + value->data = boost::container::small_vector{}; } } -template F> +template F> char const * ikarus_value_base_to_string(V const * value, F transformer) { return boost::variant2::visit( - boost::make_overloaded_function( + cppbase::overloaded { [](boost::variant2::monostate const&) -> char const * { return nullptr; }, - [&transformer](boost::container::small_vector const& data + [&transformer](auto const& data ) -> char const * { auto buffer = fmt::memory_buffer{}; @@ -108,7 +107,7 @@ char const * ikarus_value_base_to_string(V const * value, F transformer) { return buffer.data(); } - ), + }, value->data ); } From 5da995b47e6e617fba177118d5174548697bab5b Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 26 Dec 2023 13:09:41 +0100 Subject: [PATCH 029/166] make object fields public and fixup compile errors Signed-off-by: Folling --- .clang-tidy | 2 +- include/ikarus/objects/blueprint.h | 24 +-- include/ikarus/objects/entity.h | 6 +- include/ikarus/objects/object_type.h | 6 + src/objects/blueprint.cpp | 251 +++++++++++---------------- src/objects/entity.cpp | 21 +++ src/objects/object.cpp | 16 +- src/objects/object.hpp | 15 +- src/objects/object_helper.hpp | 68 ++++++++ src/objects/object_type.cpp | 12 ++ src/objects/properties/property.cpp | 32 ++-- src/persistence/project.cpp | 9 +- src/persistence/project.hpp | 12 +- 13 files changed, 258 insertions(+), 216 deletions(-) create mode 100644 src/objects/object_helper.hpp create mode 100644 src/objects/object_type.cpp diff --git a/.clang-tidy b/.clang-tidy index fa1f43f..7262427 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,6 +1,6 @@ Checks: >- -*, - bugprone-*, + bugprone-*, -bugprone-lambda-function-name, cppcoreguidelines-*, -cppcoreguidelines-owning-memory, clang-analyzer-*, google-*, -google-readability-todo, diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 57d1a7c..ce83160 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -35,13 +35,6 @@ IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project /// \remark The blueprint must not be accessed after deletion. IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); -/// \brief Gets the number of properties of a blueprint. -/// \param blueprint The blueprint to get the number of properties of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint); - /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. /// \pre \li Must not be null. @@ -49,16 +42,17 @@ IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * bluep /// \param properties_out The buffer to write the properties to. /// \pre \li Must not be null. /// \param properties_out_size The size of the buffer. +/// \see ikarus_blueprint_get_property_count IKA_API void ikarus_blueprint_get_properties( IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size ); -/// \brief Gets the number of entities linked to a blueprint. -/// \param blueprint The blueprint to get the number of linked entities of. +/// \brief Gets the number of properties of a blueprint. +/// \param blueprint The blueprint to get the number of properties of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \return The number of linked entities or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint); +/// \return The number of properties or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint); /// \brief Gets the entities linked to a blueprint. /// \param blueprint The blueprint to get the linked entities of. @@ -67,10 +61,18 @@ IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * /// \param entities_out The buffer to write the entities to. /// \pre \li Must not be null. /// \param entities_out_size The size of the buffer. +/// \see ikarus_blueprint_get_linked_entity_count IKA_API void ikarus_blueprint_get_linked_entities( IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size ); +/// \brief Gets the number of entities linked to a blueprint. +/// \param blueprint The blueprint to get the number of linked entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of linked entities or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint); + /// \brief Casts a blueprint to an object. /// \param blueprint The blueprint to cast. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 5ce2266..782452e 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -45,13 +45,9 @@ struct IkarusEntity; /// \param name The name of the entity. /// \pre \li Must not be null. /// \pre \li Must not be empty. -/// \param blueprints Blueprints to link the entity to (0..n). Null is treated as an empty array. -/// \param blueprints_count The number of blueprints. /// \return The created entity or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusEntity * ikarus_entity_create( - struct IkarusProject * project, char const * name, struct IkarusBlueprint ** blueprints, size_t blueprints_count -); +IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name); /// \brief Deletes an entity. /// \param entity The entity to delete. diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index efb6609..4298a9b 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -22,6 +22,12 @@ enum IkarusObjectType { IkarusObjectType_Property = 0b00000011, }; +/// \brief Converts an IkarusObjectType to a string. +/// \param type The type to convert. +/// \return The string representation of the type. +/// \remark The returned string must not be freed. +char const * ikarus_object_type_to_string(IkarusObjectType type); + IKARUS_END_HEADER /// @} diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index bdbea93..fc49c71 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -1,13 +1,16 @@ +#include "ikarus/objects/blueprint.h" + +#include "objects/blueprint.hpp" + #include #include #include #include -#include - -#include #include +#include +#include #include #include @@ -26,40 +29,9 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - VTRYRV( - auto const id, - nullptr, - project->get_db() - ->transact([name](auto * db) -> cppbase::Result { - LOG_VERBOSE("creating blueprint in objects table"); - - TRY(db->execute( - "INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", - static_cast(IkarusObjectType_Blueprint), - name - )); - - auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - - LOG_DEBUG("blueprint is {}", id); - - LOG_VERBOSE("inserting blueprint into blueprints table"); - - TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?);", id)); - - return cppbase::ok(id); - }) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("unable to insert blueprint into database: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_VERBOSE("successfully created blueprint"); + VTRYRV(auto const id, nullptr, insert_object(project, ctx, IkarusObjectType_Blueprint, name, [](auto * db, IkarusId id) { + return db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id); + })); return project->get_blueprint(id); } @@ -67,60 +39,11 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { LOG_INFO("deleting blueprint"); - LOG_DEBUG("project={};blueprint={}", blueprint->get_project()->get_path().c_str(), blueprint->get_id()); + LOG_DEBUG("project={}; blueprint={}", blueprint->project->get_path().c_str(), blueprint->id); - auto * ctx = blueprint->get_project()->get_function_context(); + auto * ctx = blueprint->project->get_function_context(); - TRYRV( - , - blueprint->get_project() - ->get_db() - ->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->get_id()) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to delete blueprint from objects table: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_VERBOSE("blueprint was successfully deleted from database, freeing"); - - blueprint->get_project()->uncache_blueprint(blueprint); - - LOG_VERBOSE("successfully deleted blueprint"); -} - -size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { - LOG_VERBOSE("fetching blueprint property count"); - - auto * ctx = blueprint->get_project()->get_function_context(); - - LOG_DEBUG("blueprint={}", blueprint->get_id()); - - VTRYRV( - auto count, - 0, - blueprint->get_project() - ->get_db() - ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->get_id()) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint property count: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint property count: {}", count); - - LOG_VERBOSE("successfully fetched blueprint property count"); - - return static_cast(count); + delete_object(blueprint->project, ctx, blueprint); } void ikarus_blueprint_get_properties( @@ -129,25 +52,25 @@ void ikarus_blueprint_get_properties( LOG_VERBOSE("fetching blueprint properties"); LOG_DEBUG( - "project={};blueprint={};properties_out_size={}", - blueprint->get_project()->get_path().c_str(), - blueprint->get_id(), + "project={}; blueprint={}; properties_out_size={}", + blueprint->project->get_path().c_str(), + blueprint->id, properties_out_size ); - auto * ctx = blueprint->get_project()->get_function_context(); + auto * ctx = blueprint->project->get_function_context(); IkarusId ids[properties_out_size]; TRYRV( , - blueprint->get_project() + blueprint->project ->get_db() ->query_many_buffered( "SELECT `id` FROM `properties` WHERE `source` = ?", static_cast(ids), properties_out_size, - blueprint->get_id() + blueprint->id ) .on_error([ctx](auto const& err) { ctx->set_error( @@ -167,7 +90,7 @@ void ikarus_blueprint_get_properties( VTRYRV( auto const type, , - IkarusProperty::get_property_type(blueprint->get_project(), id).on_error([ctx, id](auto const& err) { + IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const& err) { ctx->set_error( fmt::format("failed to fetch property {}'s type: {}", id, err), true, @@ -178,25 +101,101 @@ void ikarus_blueprint_get_properties( ); /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - properties_out[i] = blueprint->get_project()->get_property(id, type); + properties_out[i] = blueprint->project->get_property(id, type); } LOG_VERBOSE("successfully fetched blueprint properties"); } -size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { - LOG_VERBOSE("fetching blueprint linked entity count"); +size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { + LOG_VERBOSE("fetching blueprint property count"); - LOG_DEBUG("project={};blueprint={}", blueprint->get_project()->get_path().c_str(), blueprint->get_id()); + auto * ctx = blueprint->project->get_function_context(); - auto * ctx = blueprint->get_project()->get_function_context(); + LOG_DEBUG("blueprint={}", blueprint->id); VTRYRV( auto count, 0, - blueprint->get_project() + blueprint->project ->get_db() - ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->get_id()) + ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->id) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint property count: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_DEBUG("blueprint property count: {}", count); + + LOG_VERBOSE("successfully fetched blueprint property count"); + + return static_cast(count); +} + +void ikarus_blueprint_get_linked_entities( + IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size +) { + LOG_VERBOSE("fetching blueprint linked entities"); + + LOG_DEBUG( + "project={}; blueprint={}; entities_out_size={}", + blueprint->project->get_path().c_str(), + blueprint->id, + entities_out_size + ); + + auto * ctx = blueprint->project->get_function_context(); + + IkarusId ids[entities_out_size]; + + TRYRV( + , + blueprint->project + ->get_db() + ->query_many_buffered( + "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", + static_cast(ids), + entities_out_size, + blueprint->id + ) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint linked entity ids: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_DEBUG("blueprint linked entities: [{}]", fmt::join(ids, ids + entities_out_size, ", ")); + + for (size_t i = 0; i < entities_out_size; ++i) { + /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + entities_out[i] = blueprint->project->get_entity(ids[i]); + } + + LOG_VERBOSE("successfully fetched blueprint linked entities"); +} + +size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { + LOG_VERBOSE("fetching blueprint linked entity count"); + + LOG_DEBUG("project={}; blueprint={}", blueprint->project->get_path().c_str(), blueprint->id); + + auto * ctx = blueprint->project->get_function_context(); + + VTRYRV( + auto count, + 0, + blueprint->project + ->get_db() + ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->id) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch blueprint linked entity count: {}", err), @@ -214,52 +213,6 @@ size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprin return static_cast(count); } -void ikarus_blueprint_get_linked_entities( - IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size -) { - LOG_VERBOSE("fetching blueprint linked entities"); - - LOG_DEBUG( - "project={};blueprint={};entities_out_size={}", - blueprint->get_project()->get_path().c_str(), - blueprint->get_id(), - entities_out_size - ); - - auto * ctx = blueprint->get_project()->get_function_context(); - - IkarusId ids[entities_out_size]; - - TRYRV( - , - blueprint->get_project() - ->get_db() - ->query_many_buffered( - "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", - static_cast(ids), - entities_out_size, - blueprint->get_id() - ) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint linked entity ids: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint linked entities: [{}]", fmt::join(ids, ids + entities_out_size, ", ")); - - for (size_t i = 0; i < entities_out_size; ++i) { - /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - entities_out[i] = blueprint->get_project()->get_entity(ids[i]); - } - - LOG_VERBOSE("successfully fetched blueprint linked entities"); -} - IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint) { return static_cast(blueprint); } diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp index e69de29..ce5d853 100644 --- a/src/objects/entity.cpp +++ b/src/objects/entity.cpp @@ -0,0 +1,21 @@ +#include "entity.hpp" + +#include + +#include +#include + +IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name) { + LOG_INFO("creating new entity"); + + LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); + + auto * ctx = project->get_function_context(); + + if (cppbase::is_empty_or_blank(name)) { + ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return nullptr; + } + + // TODO +} diff --git a/src/objects/object.cpp b/src/objects/object.cpp index 2ce961e..204c7a7 100644 --- a/src/objects/object.cpp +++ b/src/objects/object.cpp @@ -1,17 +1,5 @@ #include "object.hpp" IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): - _project{project}, - _id{id} {} - -IkarusProject * IkarusObject::get_project() { - return _project; -} - -IkarusProject * IkarusObject::get_project() const { - return _project; -} - -IkarusId IkarusObject::get_id() const { - return _id; -} + project{project}, + id{id} {} diff --git a/src/objects/object.hpp b/src/objects/object.hpp index bb28da5..5a94aa2 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -1,5 +1,9 @@ #pragma once +#include + +#include + #include struct IkarusObject { @@ -15,13 +19,6 @@ public: virtual ~IkarusObject() = default; public: - [[nodiscard]] struct IkarusProject * get_project(); - - [[nodiscard]] struct IkarusProject * get_project() const; - - [[nodiscard]] IkarusId get_id() const; - -private: - struct IkarusProject mutable * _project; - IkarusId _id; + struct IkarusProject * project; + IkarusId id; }; diff --git a/src/objects/object_helper.hpp b/src/objects/object_helper.hpp new file mode 100644 index 0000000..0fe9df7 --- /dev/null +++ b/src/objects/object_helper.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include + +#include + +#include +#include + +#include + +template F> +[[nodiscard]] cppbase::Result insert_object( + IkarusProject * project, FunctionContext * ctx, IkarusObjectType type, std::string_view name, F insert_function +) { + char const * object_type_str = ikarus_object_type_to_string(type); + + VTRY( + auto const id, + project->get_db() + ->transact([&](auto * db) -> cppbase::Result { + LOG_VERBOSE("creating {} in objects table", object_type_str); + + TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(type), name)); + + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); + + LOG_DEBUG("{} is {}", object_type_str, id); + + TRY(insert_function(db, id)); + + return cppbase::ok(id); + }) + .on_error([&](auto const& err) { + ctx->set_error( + fmt::format("unable to insert {} into database: {}", object_type_str, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_VERBOSE("successfully created blueprint"); + + return cppbase::ok(id); +} + +template +void delete_object(IkarusProject * project, FunctionContext * ctx, T * object) { + auto id = object->id; + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(id)); + + TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", id).on_error([&](auto const& err) { + ctx->set_error( + fmt::format("failed to delete {} from objects table: {}", object_type_str, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + })); + + LOG_VERBOSE("{} was successfully deleted from database, freeing", object_type_str); + + project->uncache(object); + + LOG_VERBOSE("successfully deleted {}", object_type_str); +} diff --git a/src/objects/object_type.cpp b/src/objects/object_type.cpp new file mode 100644 index 0000000..fb7d825 --- /dev/null +++ b/src/objects/object_type.cpp @@ -0,0 +1,12 @@ +#include "ikarus/objects/object_type.h" + +char const * ikarus_object_type_to_string(IkarusObjectType type) { + switch (type) { + case IkarusObjectType_None: return "none"; + case IkarusObjectType_Entity: return "entity"; + case IkarusObjectType_Blueprint: return "blueprint"; + case IkarusObjectType_Property: return "property"; + } + + return "unknown"; +} diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index 00ab888..2abef0a 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -51,15 +51,15 @@ cppbase::Result IkarusProperty: IKA_API void ikarus_property_delete(IkarusProperty * property) { LOG_INFO("deleting property"); - LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->get_project()->get_function_context(); + auto * ctx = property->project->get_function_context(); TRYRV( , - property->get_project() + property->project ->get_db() - ->execute("DELETE FROM `objects` WHERE `id` = ?", property->get_id()) + ->execute("DELETE FROM `objects` WHERE `id` = ?", property->id) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to delete property from objects table: {}", err), @@ -72,7 +72,7 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { LOG_VERBOSE("property was successfully deleted from database, freeing"); - property->get_project()->uncache_property(property); + property->project->uncache(property); LOG_VERBOSE("successfully deleted property"); } @@ -80,23 +80,23 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { LOG_VERBOSE("fetching property type"); - return IkarusProperty::get_property_type(property->get_project(), property->get_id()) + return IkarusProperty::get_property_type(property->project, property->id) .unwrap_value_or(IkarusPropertyType_Toggle); } IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { LOG_VERBOSE("fetching property source"); - LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->get_project()->get_function_context(); + auto * ctx = property->project->get_function_context(); VTRYRV( auto const source, nullptr, - property->get_project() + property->project ->get_db() - ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->get_id()) + ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch property's source: {}", err), @@ -108,8 +108,8 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p ); switch (ikarus_id_get_object_type(source)) { - case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->get_project()->get_blueprint(source)}; - case IkarusObjectType_Entity: return new IkarusPropertySource{property->get_project()->get_entity(source)}; + case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->project->get_blueprint(source)}; + case IkarusObjectType_Entity: return new IkarusPropertySource{property->project->get_entity(source)}; default: { ctx->set_error( fmt::format("PropertySource is neither blueprint nor entity"), @@ -126,16 +126,16 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) { LOG_VERBOSE("fetching property default value"); - LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->get_project()->get_function_context(); + auto * ctx = property->project->get_function_context(); VTRYRV( auto const value, nullptr, - property->get_project() + property->project ->get_db() - ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->get_id()) + ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch property's default value: {}", err), diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index d7d5e5e..163bc93 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -2,7 +2,10 @@ #include "ikarus/persistence/project.h" +#include +#include #include +#include #include #include #include @@ -31,7 +34,7 @@ IkarusBlueprint * IkarusProject::get_blueprint(IkarusId id) { return get_cached_object(id, this->_blueprints); } -auto IkarusProject::uncache_blueprint(IkarusBlueprint * blueprint) -> void { +auto IkarusProject::uncache(IkarusBlueprint * blueprint) -> void { remove_cached_object(blueprint, _blueprints); } @@ -39,7 +42,7 @@ auto IkarusProject::get_entity(IkarusId id) -> IkarusEntity * { return get_cached_object(id, this->_entities); } -auto IkarusProject::uncache_entity(IkarusEntity * entity) -> void { +auto IkarusProject::uncache(IkarusEntity * entity) -> void { remove_cached_object(entity, _entities); } @@ -60,6 +63,6 @@ auto IkarusProject::get_property(IkarusId id, IkarusPropertyType type) -> Ikarus return iter->second.get(); } -auto IkarusProject::uncache_property(IkarusProperty * property) -> void { +auto IkarusProject::uncache(IkarusProperty * property) -> void { remove_cached_object(property, _properties); } diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index d3fc6a1..2af1fff 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -11,10 +11,6 @@ #include #include -#include -#include -#include - constexpr inline auto MAXIMUM_ERROR_INFOS = 8; /// \private @@ -32,13 +28,13 @@ public: public: [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; - auto uncache_blueprint(struct IkarusBlueprint * blueprint) -> void; + auto uncache(struct IkarusBlueprint * blueprint) -> void; [[nodiscard]] auto get_entity(IkarusId id) -> struct IkarusEntity *; - auto uncache_entity(struct IkarusEntity * entity) -> void; + auto uncache(struct IkarusEntity * entity) -> void; [[nodiscard]] auto get_property(IkarusId id, IkarusPropertyType type) -> struct IkarusProperty *; - auto uncache_property(struct IkarusProperty * property) -> void; + auto uncache(struct IkarusProperty * property) -> void; private: template @@ -54,7 +50,7 @@ private: template void remove_cached_object(T * object, std::unordered_map>& cache) { - cache.erase(object->get_id()); + cache.erase(object->id); } private: From 7acda2852fd9a7ba031bc05d6d66c1dcb44bf6ae Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 27 Dec 2023 12:52:14 +0100 Subject: [PATCH 030/166] update sqlitecpp Signed-off-by: Folling --- src/objects/object_helper.hpp | 68 --------------- src/objects/util.hpp | 151 ++++++++++++++++++++++++++++++++++ vendor/sqlitecpp | 2 +- 3 files changed, 152 insertions(+), 69 deletions(-) delete mode 100644 src/objects/object_helper.hpp create mode 100644 src/objects/util.hpp diff --git a/src/objects/object_helper.hpp b/src/objects/object_helper.hpp deleted file mode 100644 index 0fe9df7..0000000 --- a/src/objects/object_helper.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include - -#include - -template F> -[[nodiscard]] cppbase::Result insert_object( - IkarusProject * project, FunctionContext * ctx, IkarusObjectType type, std::string_view name, F insert_function -) { - char const * object_type_str = ikarus_object_type_to_string(type); - - VTRY( - auto const id, - project->get_db() - ->transact([&](auto * db) -> cppbase::Result { - LOG_VERBOSE("creating {} in objects table", object_type_str); - - TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(type), name)); - - auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - - LOG_DEBUG("{} is {}", object_type_str, id); - - TRY(insert_function(db, id)); - - return cppbase::ok(id); - }) - .on_error([&](auto const& err) { - ctx->set_error( - fmt::format("unable to insert {} into database: {}", object_type_str, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_VERBOSE("successfully created blueprint"); - - return cppbase::ok(id); -} - -template -void delete_object(IkarusProject * project, FunctionContext * ctx, T * object) { - auto id = object->id; - auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(id)); - - TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", id).on_error([&](auto const& err) { - ctx->set_error( - fmt::format("failed to delete {} from objects table: {}", object_type_str, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - })); - - LOG_VERBOSE("{} was successfully deleted from database, freeing", object_type_str); - - project->uncache(object); - - LOG_VERBOSE("successfully deleted {}", object_type_str); -} diff --git a/src/objects/util.hpp b/src/objects/util.hpp new file mode 100644 index 0000000..963701b --- /dev/null +++ b/src/objects/util.hpp @@ -0,0 +1,151 @@ +#pragma once + +#include + +#include + +#include +#include + +#include + +namespace util { +template F> +[[nodiscard]] cppbase::Result insert_object( + IkarusProject * project, FunctionContext * ctx, IkarusObjectType type, std::string_view name, F insert_function +) { + char const * object_type_str = ikarus_object_type_to_string(type); + + VTRY( + auto const id, + project->get_db() + ->transact([&](auto * db) -> cppbase::Result { + LOG_VERBOSE("creating {} in objects table", object_type_str); + + TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(type), name)); + + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); + + LOG_DEBUG("{} is {}", object_type_str, id); + + TRY(insert_function(db, id)); + + return cppbase::ok(id); + }) + .on_error([&](auto const& err) { + ctx->set_error( + fmt::format("unable to insert {} into database: {}", object_type_str, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_VERBOSE("successfully created blueprint"); + + return cppbase::ok(id); +} + +template +void delete_object(IkarusProject * project, FunctionContext * ctx, T * object) { + auto id = object->id; + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(id)); + + TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", id).on_error([&](auto const& err) { + ctx->set_error( + fmt::format("failed to delete {} from objects table: {}", object_type_str, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + })); + + LOG_VERBOSE("{} was successfully deleted from database, freeing", object_type_str); + + project->uncache(object); + + LOG_VERBOSE("successfully deleted {}", object_type_str); +} + +struct SingleQueryData { + std::string_view table_name; + std::string_view select_field_name; +}; + +template +cppbase::Result fetch_single_field(IkarusObject * object, SingleQueryData const& query_data) { + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); + + LOG_VERBOSE("fetching property default value"); + + LOG_VERBOSE("project={};property={}", object->project->get_path().c_str(), object->id); + + auto * ctx = object->project->get_function_context(); + + VTRY( + T value, + object->project->get_db() + ->query_one( + fmt::format("SELECT `{}` FROM `{}` WHERE `id` = ?", query_data.select_field_name, query_data.table_name), + object->id + ) + .on_error([&](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.select_field_name, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + return value; +} + +struct MultipleBufferQueryData { + std::string_view table_name; + std::string_view select_field_name; + std::string_view where_field_name; +}; + +template +cppbase::Result fetch_multiple_buffered( + IkarusObject * object, + MultipleBufferQueryData const& query_data, + std::string_view relation_desc, + T * buffer, + size_t buffer_size +) { + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); + + LOG_VERBOSE("fetching {} {}", object_type_str, relation_desc); + + LOG_VERBOSE("project={};{}={}", object->project->get_path().c_str(), object_type_str, object->id); + + auto * ctx = object->project->get_function_context(); + + TRY(object->project->get_db() + ->query_many_buffered( + fmt::format( + "SELECT `{}` FROM `{}` WHERE `{}` = ?", + query_data.select_field_name, + query_data.table_name, + query_data.where_field_name + ), + buffer, + buffer_size, + object->id + ) + .on_error([&](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch {} {} from database: {}", object_type_str, relation_desc, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + })); + + return cppbase::ok(); +} +} diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 6e39dd2..2a93b8b 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 6e39dd241f8469784b8a356e7a9563fa58b0e2c4 +Subproject commit 2a93b8b1a8be03a9a9c4c72956b1111299de0ddd From 0496ea7259208652b52e7880fe9c4caa17dd33d1 Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 27 Dec 2023 12:52:27 +0100 Subject: [PATCH 031/166] simplify blueprint implementation with helpers Signed-off-by: Folling --- src/objects/blueprint.cpp | 240 ++++++++------------------- src/objects/util.hpp | 152 +++++++++++++---- src/persistence/function_context.cpp | 4 +- src/persistence/function_context.hpp | 14 +- src/persistence/project.cpp | 2 +- src/persistence/project.hpp | 6 +- 6 files changed, 200 insertions(+), 218 deletions(-) diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index fc49c71..af5fc08 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -1,16 +1,15 @@ #include "ikarus/objects/blueprint.h" #include "objects/blueprint.hpp" - -#include +#include "util.hpp" #include #include #include #include -#include #include +#include #include #include @@ -18,199 +17,92 @@ IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusObject{project, id} {} IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { - LOG_INFO("creating new blueprint"); - - LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); - - auto * ctx = project->get_function_context(); - - if (cppbase::is_empty_or_blank(name)) { - ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - VTRYRV(auto const id, nullptr, insert_object(project, ctx, IkarusObjectType_Blueprint, name, [](auto * db, IkarusId id) { - return db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id); - })); - - return project->get_blueprint(id); + return ikarus::util::insert_object( + project, + IkarusObjectType_Blueprint, + name, + [](auto * db, IkarusId id) { return db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id); }, + [project](IkarusId id) { return project->get_blueprint(id); } + ).unwrap_value_or(nullptr); } void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { - LOG_INFO("deleting blueprint"); - - LOG_DEBUG("project={}; blueprint={}", blueprint->project->get_path().c_str(), blueprint->id); - - auto * ctx = blueprint->project->get_function_context(); - - delete_object(blueprint->project, ctx, blueprint); + ikarus::util::delete_object(blueprint->project, blueprint); } void ikarus_blueprint_get_properties( IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size ) { - LOG_VERBOSE("fetching blueprint properties"); + ikarus::util::fetch_multiple_buffered( + blueprint, + ikarus::util::MultipleBufferQueryData{ + .table_name = "properties", + .select_field_name = "id", + .where_field_name = "blueprint", + .relation_desc = "properties" + }, + properties_out, + properties_out_size, + [&](IkarusProject * project, IkarusFunctionContext * ctx, IkarusId id + ) -> cppbase::Result { + VTRY( + auto const type, IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch property {}'s type: {}", id, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); - LOG_DEBUG( - "project={}; blueprint={}; properties_out_size={}", - blueprint->project->get_path().c_str(), - blueprint->id, - properties_out_size + return cppbase::ok(project->get_property(id, type)); + } ); - - auto * ctx = blueprint->project->get_function_context(); - - IkarusId ids[properties_out_size]; - - TRYRV( - , - blueprint->project - ->get_db() - ->query_many_buffered( - "SELECT `id` FROM `properties` WHERE `source` = ?", - static_cast(ids), - properties_out_size, - blueprint->id - ) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint property ids: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint properties: [{}]", fmt::join(ids, ids + properties_out_size, ", ")); - - for (size_t i = 0; i < properties_out_size; ++i) { - auto const id = ids[i]; - - VTRYRV( - auto const type, - , - IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch property {}'s type: {}", id, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - properties_out[i] = blueprint->project->get_property(id, type); - } - - LOG_VERBOSE("successfully fetched blueprint properties"); } size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { - LOG_VERBOSE("fetching blueprint property count"); - - auto * ctx = blueprint->project->get_function_context(); - - LOG_DEBUG("blueprint={}", blueprint->id); - - VTRYRV( - auto count, - 0, - blueprint->project - ->get_db() - ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->id) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint property count: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint property count: {}", count); - - LOG_VERBOSE("successfully fetched blueprint property count"); - - return static_cast(count); + return ikarus::util::fetch_count( + blueprint, + ikarus::util::CountQueryData{ + .table_name = "blueprint_properties", + .select_field_name = "property", + .where_field_name = "blueprint", + .relation_desc = "properties" + } + ) + .unwrap_value_or(0); } void ikarus_blueprint_get_linked_entities( IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size ) { - LOG_VERBOSE("fetching blueprint linked entities"); - - LOG_DEBUG( - "project={}; blueprint={}; entities_out_size={}", - blueprint->project->get_path().c_str(), - blueprint->id, - entities_out_size + ikarus::util::fetch_multiple_buffered( + blueprint, + ikarus::util::MultipleBufferQueryData{ + .table_name = "entity_blueprint_links", + .select_field_name = "entity", + .where_field_name = "blueprint", + .relation_desc = "linked entities" + }, + entities_out, + entities_out_size, + [&](IkarusProject * project, [[maybe_unused]] IkarusFunctionContext * ctx, IkarusId id + ) -> cppbase::Result { return cppbase::ok(project->get_entity(id)); } ); - - auto * ctx = blueprint->project->get_function_context(); - - IkarusId ids[entities_out_size]; - - TRYRV( - , - blueprint->project - ->get_db() - ->query_many_buffered( - "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", - static_cast(ids), - entities_out_size, - blueprint->id - ) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint linked entity ids: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint linked entities: [{}]", fmt::join(ids, ids + entities_out_size, ", ")); - - for (size_t i = 0; i < entities_out_size; ++i) { - /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - entities_out[i] = blueprint->project->get_entity(ids[i]); - } - - LOG_VERBOSE("successfully fetched blueprint linked entities"); } size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { - LOG_VERBOSE("fetching blueprint linked entity count"); - - LOG_DEBUG("project={}; blueprint={}", blueprint->project->get_path().c_str(), blueprint->id); - - auto * ctx = blueprint->project->get_function_context(); - - VTRYRV( - auto count, - 0, - blueprint->project - ->get_db() - ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->id) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint linked entity count: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint linked entity count: {}", count); - - LOG_VERBOSE("successfully fetched blueprint linked entity count: {}", count); - - return static_cast(count); + return ikarus::util::fetch_count( + blueprint, + ikarus::util::CountQueryData{ + .table_name = "entity_blueprint_links", + .select_field_name = "entity", + .where_field_name = "blueprint", + .relation_desc = "linked entities" + } + ) + .unwrap_value_or(0); } IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint) { diff --git a/src/objects/util.hpp b/src/objects/util.hpp index 963701b..f192b7a 100644 --- a/src/objects/util.hpp +++ b/src/objects/util.hpp @@ -1,20 +1,43 @@ #pragma once +#include "util.hpp" + #include #include +#include #include #include #include -namespace util { -template F> -[[nodiscard]] cppbase::Result insert_object( - IkarusProject * project, FunctionContext * ctx, IkarusObjectType type, std::string_view name, F insert_function +namespace ikarus::util { + +struct EmptyNameError {}; + +COMPOUND_ERROR(InsertObjectError, EmptyNameError, sqlitecpp::TransactionError); + +template +[[nodiscard]] cppbase::Result, InsertObjectError> insert_object( + IkarusProject * project, + IkarusObjectType type, + std::string_view name, + InsertFunction insert_function, + ObjectFactory object_factory ) { - char const * object_type_str = ikarus_object_type_to_string(type); + auto const * object_type_str = ikarus_object_type_to_string(type); + + LOG_INFO("creating new {}", object_type_str); + + LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); + + auto * ctx = project->get_function_context(); + + if (cppbase::is_empty_or_blank(name)) { + ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return cppbase::err(EmptyNameError{}); + } VTRY( auto const id, @@ -42,17 +65,23 @@ template F> }) ); - LOG_VERBOSE("successfully created blueprint"); + LOG_VERBOSE("successfully created {}", object_type_str); - return cppbase::ok(id); + return cppbase::ok(object_factory(id)); } -template -void delete_object(IkarusProject * project, FunctionContext * ctx, T * object) { - auto id = object->id; - auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(id)); +template + requires std::derived_from +void delete_object(IkarusProject * project, Object * object) { + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", id).on_error([&](auto const& err) { + LOG_INFO("deleting {}", object_type_str); + + LOG_DEBUG("project={}; {}={}", object_type_str, object->project->get_path().c_str(), object->id); + + auto * ctx = object->project->get_function_context(); + + TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const& err) { ctx->set_error( fmt::format("failed to delete {} from objects table: {}", object_type_str, err), true, @@ -73,8 +102,9 @@ struct SingleQueryData { std::string_view select_field_name; }; -template -cppbase::Result fetch_single_field(IkarusObject * object, SingleQueryData const& query_data) { +template + requires std::derived_from +cppbase::Result fetch_single_field(Object const * object, SingleQueryData const& query_data) { auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); LOG_VERBOSE("fetching property default value"); @@ -86,7 +116,7 @@ cppbase::Result fetch_single_field(IkarusObject VTRY( T value, object->project->get_db() - ->query_one( + ->template query_one( fmt::format("SELECT `{}` FROM `{}` WHERE `id` = ?", query_data.select_field_name, query_data.table_name), object->id ) @@ -107,45 +137,105 @@ struct MultipleBufferQueryData { std::string_view table_name; std::string_view select_field_name; std::string_view where_field_name; + std::string_view relation_desc; }; -template -cppbase::Result fetch_multiple_buffered( - IkarusObject * object, - MultipleBufferQueryData const& query_data, - std::string_view relation_desc, - T * buffer, - size_t buffer_size -) { +template + requires std::derived_from +void fetch_multiple_buffered( + Object const * object, MultipleBufferQueryData const& query_data, Mapped * mapped_buffer, size_t buffer_size, F transformer +) + requires cppbase:: + is_result_with_value_type_v> +{ + auto * ctx = object->project->get_function_context(); + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - LOG_VERBOSE("fetching {} {}", object_type_str, relation_desc); + LOG_VERBOSE("fetching {} {}", object_type_str, query_data.relation_desc); LOG_VERBOSE("project={};{}={}", object->project->get_path().c_str(), object_type_str, object->id); - auto * ctx = object->project->get_function_context(); + Selected select_buffer[buffer_size]; - TRY(object->project->get_db() - ->query_many_buffered( + TRYRV( + , + object->project->get_db() + ->template query_many_buffered( fmt::format( "SELECT `{}` FROM `{}` WHERE `{}` = ?", query_data.select_field_name, query_data.table_name, query_data.where_field_name ), - buffer, + select_buffer, buffer_size, object->id ) .on_error([&](auto const& err) { ctx->set_error( - fmt::format("failed to fetch {} {} from database: {}", object_type_str, relation_desc, err), + fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.relation_desc, err), true, IkarusErrorInfo_Source_SubSystem, IkarusErrorInfo_Type_SubSystem_Database ); - })); + }) + ); - return cppbase::ok(); + LOG_DEBUG( + "{} {}: [{}]", object_type_str, query_data.relation_desc, fmt::join(select_buffer, select_buffer + buffer_size, ", ") + ); + + for (size_t i = 0; i < buffer_size; ++i) { + VTRYRV(mapped_buffer[i], , transformer(object->project, ctx, select_buffer[i])); + } } + +struct CountQueryData { + std::string_view table_name; + std::string_view select_field_name; + std::string_view where_field_name; + std::string_view relation_desc; +}; + +template + requires std::derived_from +cppbase::Result fetch_count(Object const * object, CountQueryData const& query_data) { + auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); + + LOG_VERBOSE("fetching {} {} count", object_type_str, query_data.relation_desc); + + auto * ctx = object->project->get_function_context(); + + LOG_DEBUG("{}={}", object_type_str, object->id); + + VTRY( + auto count, + object->project->get_db() + ->template query_one( + fmt::format( + "SELECT COUNT(`{}`) FROM `{}` WHERE `{}` = ?;", + query_data.select_field_name, + query_data.table_name, + query_data.where_field_name + ), + object->id + ) + .on_error([&](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch {} {} count: {}", object_type_str, query_data.relation_desc, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_DEBUG("{} {} count: {}", object_type_str, query_data.relation_desc, count); + + LOG_VERBOSE("successfully fetched {} {} count", object_type_str, query_data.relation_desc); + + return cppbase::ok(static_cast(count)); +} + } diff --git a/src/persistence/function_context.cpp b/src/persistence/function_context.cpp index 96edddf..f056308 100644 --- a/src/persistence/function_context.cpp +++ b/src/persistence/function_context.cpp @@ -1,9 +1,9 @@ #include "function_context.hpp" -FunctionContext::FunctionContext(IkarusProject * project): +IkarusFunctionContext::IkarusFunctionContext(IkarusProject * project): _project{project} {} -FunctionContext::~FunctionContext() { +IkarusFunctionContext::~IkarusFunctionContext() { if (_project->_function_contexts.size() == 1) { if (_project->error_message_buffer.empty()) { _project->error_message_buffer.push_back('\0'); diff --git a/src/persistence/function_context.hpp b/src/persistence/function_context.hpp index 3dc64b3..769006b 100644 --- a/src/persistence/function_context.hpp +++ b/src/persistence/function_context.hpp @@ -12,17 +12,17 @@ #include -struct FunctionContext { +struct IkarusFunctionContext { public: - explicit FunctionContext(struct IkarusProject * project); + explicit IkarusFunctionContext(struct IkarusProject * project); - FunctionContext(FunctionContext const&) noexcept = default; - FunctionContext(FunctionContext&&) noexcept = default; + IkarusFunctionContext(IkarusFunctionContext const&) noexcept = default; + IkarusFunctionContext(IkarusFunctionContext&&) noexcept = default; - auto operator=(FunctionContext const&) noexcept -> FunctionContext& = default; - auto operator=(FunctionContext&&) noexcept -> FunctionContext& = default; + auto operator=(IkarusFunctionContext const&) noexcept -> IkarusFunctionContext& = default; + auto operator=(IkarusFunctionContext&&) noexcept -> IkarusFunctionContext& = default; - ~FunctionContext(); + ~IkarusFunctionContext(); public: template diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index 163bc93..bf1e51a 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -26,7 +26,7 @@ auto IkarusProject::get_db() const -> sqlitecpp::Connection const * { return _db.get(); } -auto IkarusProject::get_function_context() -> FunctionContext * { +auto IkarusProject::get_function_context() -> IkarusFunctionContext * { return &_function_contexts.emplace_back(this); } diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index 2af1fff..7d9d5b0 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -24,7 +24,7 @@ public: [[nodiscard]] auto get_db() const -> sqlitecpp::Connection const *; public: - [[nodiscard]] auto get_function_context() -> struct FunctionContext *; + [[nodiscard]] auto get_function_context() -> struct IkarusFunctionContext *; public: [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; @@ -54,7 +54,7 @@ private: } private: - friend struct FunctionContext; + friend struct IkarusFunctionContext; std::string _name; std::filesystem::path _path; @@ -67,5 +67,5 @@ private: std::unordered_map> _properties; std::unordered_map> _entities; - std::vector _function_contexts; + std::vector _function_contexts; }; From 910f337d0216b3986e2024e8e05476a77ca866d5 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 28 Dec 2023 16:52:05 +0100 Subject: [PATCH 032/166] change clang-format Signed-off-by: Folling --- .clang-format | 65 +++++++++++-------- include/ikarus/errors.h | 8 +-- include/ikarus/global.h | 4 +- include/ikarus/objects/blueprint.h | 10 ++- include/ikarus/objects/entity.h | 26 +++++--- include/ikarus/objects/object.h | 7 +- .../objects/properties/number_property.h | 13 ++-- include/ikarus/objects/properties/property.h | 4 +- .../ikarus/objects/properties/text_property.h | 13 ++-- .../objects/properties/toggle_property.h | 13 ++-- include/ikarus/persistence/project.h | 9 +-- include/ikarus/values/value.h | 4 +- src/objects/blueprint.cpp | 29 +++++---- src/objects/blueprint.hpp | 8 +-- src/objects/entity.hpp | 8 +-- src/objects/object.hpp | 8 +-- src/objects/properties/property.cpp | 61 +++++++---------- src/objects/properties/property.hpp | 17 +++-- src/objects/properties/property_source.hpp | 8 +-- src/objects/properties/toggle_property.cpp | 7 +- src/objects/util.hpp | 29 +++++---- src/persistence/function_context.hpp | 14 ++-- src/persistence/project.cpp | 5 +- src/persistence/project.hpp | 6 +- src/values/number_value.cpp | 2 +- src/values/number_value.hpp | 11 ++-- src/values/text_value.cpp | 2 +- src/values/text_value.hpp | 11 ++-- src/values/toggle_value.cpp | 2 +- src/values/toggle_value.hpp | 11 ++-- src/values/value.cpp | 11 ++-- src/values/value.hpp | 10 +-- src/values/value_base.hpp | 35 ++++------ 33 files changed, 217 insertions(+), 254 deletions(-) diff --git a/.clang-format b/.clang-format index 23c71e6..a600edb 100644 --- a/.clang-format +++ b/.clang-format @@ -15,8 +15,8 @@ AlignEscapedNewlines: Left AlignOperands: Align AlignTrailingComments: true -AllowAllArgumentsOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: true +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: true AllowShortEnumsOnASingleLine: true @@ -35,36 +35,38 @@ BinPackParameters: false BitFieldColonSpacing: Both BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false BeforeCatch: false BeforeElse: false BeforeLambdaBody: false BeforeWhile: false - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: MultiLine - AfterEnum: false - AfterExternBlock: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false IndentBraces: false SplitEmptyFunction: false SplitEmptyNamespace: false SplitEmptyRecord: false +BracedInitializerIndentWidth: 4 + +# BreakAdjacentStringLiterals: true BreakAfterAttributes: Never BreakBeforeBinaryOperators: None -BreakBeforeBraces: Attach +BreakBeforeBraces: Custom BreakBeforeConceptDeclarations: Always -# BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon BreakStringLiterals: false -ColumnLimit: 128 +ColumnLimit: 140 CompactNamespaces: false ConstructorInitializerIndentWidth: 4 @@ -77,7 +79,9 @@ DerivePointerAlignment: false EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: Always -FixNamespaceComments: false +ExperimentalAutoDetectBinPacking: true + +FixNamespaceComments: true IncludeBlocks: Regroup IncludeCategories: @@ -112,54 +116,59 @@ IndentAccessModifiers: false IndentCaseBlocks: false IndentCaseLabels: false IndentExternBlock: NoIndent -IndentGotoLabels: true +IndentGotoLabels: false IndentPPDirectives: None IndentRequiresClause: true IndentWidth: 4 IndentWrappedFunctionNames: false InsertBraces: true InsertNewlineAtEOF: true +InsertTrailingCommas: Wrapped -# InsertNewlineAtEOF: true -# IntegerLiteralSeparator: -# Binary: 0 -# Decimal: 3 -# Hex: -1 +IntegerLiteralSeparator: + Binary: -1 + Decimal: 3 + Hex: -1 +KeepEmptyLinesAtEOF: false KeepEmptyLinesAtTheStartOfBlocks: false LambdaBodyIndentation: Signature Language: Cpp -# LineEnding: LF +LineEnding: LF MaxEmptyLinesToKeep: 1 NamespaceIndentation: None +PPIndentWidth: -1 PackConstructorInitializers: Never PointerAlignment: Middle QualifierAlignment: Right # QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ] -ReferenceAlignment: Left +ReferenceAlignment: Middle ReflowComments: true -# RemoveSemicolon: true +RemoveBracesLLVM: false +RemoveParentheses: MultipleParentheses +RemoveSemicolon: true RequiresClausePosition: OwnLine -# RequiresExpressionIndentation: OuterScope +RequiresExpressionIndentation: OuterScope SeparateDefinitionBlocks: Always SortIncludes: CaseInsensitive -SortUsingDeclarations: true +SortUsingDeclarations: LexicographicNumeric SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: false - +SpaceAroundPointerQualifiers: Both SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: false SpaceBeforeInheritanceColon: true diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index 9dc876d..3218d74 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -15,10 +15,10 @@ IKARUS_BEGIN_HEADER /// \brief Delineates what caused an error. -/// \details First 2 bytes delineate the major type, next 2 bytes delineate the minor type, next 4 bytes delineate the detail -/// type. -/// \remark Note that this doesn't show responsibility. An error with source "SubSystem" could still be the fault of -/// libikarus. +/// \details First 2 bytes delineate the major type, next 2 bytes delineate the minor type, next 4 bytes delineate the +/// detail type. +/// \remark Note that this doesn't show responsibility. An error with source "SubSystem" could still be the +/// fault of libikarus. enum IkarusErrorInfo { /// \brief No error occurred. IkarusErrorInfo_Source_None = 0x0001000000000000, diff --git a/include/ikarus/global.h b/include/ikarus/global.h index bf9e5d5..4665863 100644 --- a/include/ikarus/global.h +++ b/include/ikarus/global.h @@ -11,8 +11,8 @@ IKARUS_BEGIN_HEADER -/// \brief Frees a pointer allocated by ikarus. Every pointer returned by a function must be freed using this function unless -/// explicitly stated otherwise. +/// \brief Frees a pointer allocated by ikarus. Every pointer returned by a function must be freed using this function +/// unless explicitly stated otherwise. IKA_API void ikarus_free(void * ptr); IKARUS_END_HEADER diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index ce83160..2163e6f 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -43,9 +43,8 @@ IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); /// \pre \li Must not be null. /// \param properties_out_size The size of the buffer. /// \see ikarus_blueprint_get_property_count -IKA_API void ikarus_blueprint_get_properties( - IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size -); +IKA_API void +ikarus_blueprint_get_properties(IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size); /// \brief Gets the number of properties of a blueprint. /// \param blueprint The blueprint to get the number of properties of. @@ -62,9 +61,8 @@ IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * bluep /// \pre \li Must not be null. /// \param entities_out_size The size of the buffer. /// \see ikarus_blueprint_get_linked_entity_count -IKA_API void ikarus_blueprint_get_linked_entities( - IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size -); +IKA_API void +ikarus_blueprint_get_linked_entities(IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size); /// \brief Gets the number of entities linked to a blueprint. /// \param blueprint The blueprint to get the number of linked entities of. diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 782452e..3e38e25 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -56,6 +56,14 @@ IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char /// \remark The entity must not be accessed after deletion. IKA_API void ikarus_entity_delete(IkarusEntity * entity); +/// \brief Checks if an entity is linked to a blueprint. +/// \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. +/// \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); /// \brief Links an entity to a blueprint. @@ -68,8 +76,8 @@ IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, s /// \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); -/// \brief Unlinks an entity from a blueprint. All values of the properties of the blueprint the entity is linked with will be -/// deleted. +/// \brief Unlinks an entity from a blueprint. All values of the properties of the blueprint the entity is linked with +/// will be deleted. /// \param entity The entity to unlink. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -103,12 +111,11 @@ IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity); /// \param properties_out The buffer to write the properties to. /// \pre \li Must not be null. /// \param properties_out_size The size of the buffer. -IKA_API void ikarus_entity_get_properties( - IkarusEntity const * entity, struct IkarusProperty ** properties_out, size_t properties_out_size -); +IKA_API void ikarus_entity_get_properties(IkarusEntity const * entity, struct IkarusProperty ** properties_out, size_t properties_out_size); /// \brief Gets the value of a property of an entity. -/// \details If the entity has never set the value of the property, the default value is returned (which may be undefined). +/// \details If the entity has never set the value of the property, the default value is returned (which may be +/// undefined). /// \param entity The entity to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -116,7 +123,8 @@ IKA_API void ikarus_entity_get_properties( /// \pre \li Must not be null. /// \pre \li Must exist. /// \return The value of the property or null if the entity does not have the property or an error occurs. -/// \remark Must be freed using #ikarus_free. +/// \remark Must be freed using +/// #ikarus_free. IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property); /// \brief Sets the value of a property of an entity. @@ -131,9 +139,7 @@ IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * /// \pre \li Must be of the same type as the property. /// \pre \li Must be valid for the property's settings. /// \remark If the entity does not have the property, this function fails. -IKA_API void ikarus_entity_set_value( - IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value -); +IKA_API void ikarus_entity_set_value(IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value); /// \brief Casts an entity to an object. /// \param entity The entity to cast. diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index abaa580..ba754c5 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -19,11 +19,8 @@ IKARUS_BEGIN_HEADER struct IkarusObject; /// \brief Compares two objects for equality. -/// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the same -/// object. -/// \param lhs The left hand side object. -/// \pre \li Must not be null. -/// \param rhs The right hand side object. +/// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the +/// same object. \param lhs The left hand side object. \pre \li Must not be null. \param rhs The right hand side object. /// \pre \li Must not be null. /// \return True if the objects are equal, false otherwise. IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs); diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 9b4fa4d..3108201 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -13,9 +13,8 @@ IKARUS_BEGIN_HEADER struct IkarusNumberProperty; -IKA_API IkarusNumberProperty * ikarus_number_property_create( - struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source -); +IKA_API IkarusNumberProperty * +ikarus_number_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); /// \brief Sets the default value for a number property. /// \param property The number property. @@ -30,11 +29,9 @@ IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(stru /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values -/// and other settings. -IKA_API void ikarus_number_property_set_default_value( - struct IkarusNumberProperty * property, struct IkarusNumberValue * default_value -); +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between +/// default values and other settings. +IKA_API void ikarus_number_property_set_default_value(struct IkarusNumberProperty * property, struct IkarusNumberValue * default_value); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index c0f37fa..ef28ca2 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -33,8 +33,8 @@ IKARUS_BEGIN_HEADER /// - May be undefined /// /// Additionally, each property has a default value. If no default value is provided, a sensible default is chosen. -/// Setting a default value that isn't valid for the property is an error. Changing settings so that the current default value -/// becomes invalid is valid but unsets the custom default value. +/// Setting a default value that isn't valid for the property is an error. Changing settings so that the current default +/// value becomes invalid is valid but unsets the custom default value. /// /// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. /// The latter allows you to specify an "unknown" value for a property. diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index 0e6a435..9c91c80 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -13,9 +13,8 @@ IKARUS_BEGIN_HEADER struct IkarusTextProperty; -IKA_API IkarusTextProperty * ikarus_text_property_create( - struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source -); +IKA_API IkarusTextProperty * +ikarus_text_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); /// \brief Sets the default value for a text property. /// \param property The text property. @@ -30,11 +29,9 @@ IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct I /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values -/// and other settings. -IKA_API void ikarus_text_property_set_default_value( - struct IkarusTextProperty * property, struct IkarusTextValue * default_value -); +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between +/// default values and other settings. +IKA_API void ikarus_text_property_set_default_value(struct IkarusTextProperty * property, struct IkarusTextValue * default_value); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index 0d67304..cfa51e3 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -13,9 +13,8 @@ IKARUS_BEGIN_HEADER struct IkarusToggleProperty; -IKA_API IkarusToggleProperty * ikarus_toggle_property_create( - struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source -); +IKA_API IkarusToggleProperty * +ikarus_toggle_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); /// \brief Sets the default value for a toggle property. /// \param property The toggle property. @@ -30,11 +29,9 @@ IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(stru /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values -/// and other settings. -IKA_API void ikarus_toggle_property_set_default_value( - struct IkarusToggleProperty * property, struct IkarusToggleValue * default_value -); +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between +/// default values and other settings. +IKA_API void ikarus_toggle_property_set_default_value(struct IkarusToggleProperty * property, struct IkarusToggleValue * default_value); IKARUS_END_HEADER diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index a041946..7df0e45 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -144,9 +144,8 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project) /// \param blueprints_out The buffer to write the blueprints to. /// \pre \li Must not be null. /// \param blueprints_out_size The size of the buffer. -IKA_API void ikarus_project_get_blueprints( - IkarusProject const * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size -); +IKA_API void +ikarus_project_get_blueprints(IkarusProject const * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size); /// \brief Gets the entity root folder of a project. /// \param project The project to get the entity root folder of. @@ -170,9 +169,7 @@ IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project); /// \param entities_out The buffer to write the entities to. /// \pre \li Must not be null. /// \param entities_out_size The size of the buffer. -IKA_API void ikarus_project_get_entities( - IkarusProject const * project, struct IkarusEntity ** entities_out, size_t entities_out_size -); +IKA_API void ikarus_project_get_entities(IkarusProject const * project, struct IkarusEntity ** entities_out, size_t entities_out_size); IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index d33f87a..880c2eb 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -12,8 +12,8 @@ /// Each value may be undefined. \see IkarusProperty /// Values are stored as lists. If a property is "singular" then its value is a list of size 1. /// Values are typed, with types existing for each of the corresponding property types. -/// When setting values for a property the type must match the property type and the value must be valid under the property's -/// settings. \see PropertyType +/// When setting values for a property the type must match the property type and the value must be valid under the +/// property's settings. \see PropertyType /// @{ IKARUS_BEGIN_HEADER diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index af5fc08..c8bf1e5 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -31,7 +31,9 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { } void ikarus_blueprint_get_properties( - IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size + IkarusBlueprint const * blueprint, + struct IkarusProperty ** properties_out, + size_t properties_out_size ) { ikarus::util::fetch_multiple_buffered( blueprint, @@ -43,18 +45,15 @@ void ikarus_blueprint_get_properties( }, properties_out, properties_out_size, - [&](IkarusProject * project, IkarusFunctionContext * ctx, IkarusId id - ) -> cppbase::Result { - VTRY( - auto const type, IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch property {}'s type: {}", id, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); + [&](IkarusProject * project, IkarusFunctionContext * ctx, IkarusId id) -> cppbase::Result { + VTRY(auto const type, IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const & err) { + ctx->set_error( + fmt::format("failed to fetch property {}'s type: {}", id, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + })); return cppbase::ok(project->get_property(id, type)); } @@ -75,7 +74,9 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { } void ikarus_blueprint_get_linked_entities( - IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size + IkarusBlueprint const * blueprint, + struct IkarusEntity ** entities_out, + size_t entities_out_size ) { ikarus::util::fetch_multiple_buffered( blueprint, diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index 6fa6281..61aca64 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -5,11 +5,11 @@ struct IkarusBlueprint : IkarusObject { IkarusBlueprint(struct IkarusProject * project, IkarusId id); - IkarusBlueprint(IkarusBlueprint const&) = default; - IkarusBlueprint(IkarusBlueprint&&) = default; + IkarusBlueprint(IkarusBlueprint const &) = default; + IkarusBlueprint(IkarusBlueprint &&) = default; - IkarusBlueprint& operator=(IkarusBlueprint const&) = default; - IkarusBlueprint& operator=(IkarusBlueprint&&) = default; + IkarusBlueprint & operator=(IkarusBlueprint const &) = default; + IkarusBlueprint & operator=(IkarusBlueprint &&) = default; ~IkarusBlueprint() override = default; }; diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index f6d4f90..92cf4c6 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -6,11 +6,11 @@ struct IkarusEntity : IkarusObject { inline IkarusEntity(struct IkarusProject * project, IkarusId id): IkarusObject{project, id} {} - IkarusEntity(IkarusEntity const&) = default; - IkarusEntity(IkarusEntity&&) = default; + IkarusEntity(IkarusEntity const &) = default; + IkarusEntity(IkarusEntity &&) = default; - IkarusEntity& operator=(IkarusEntity const&) = default; - IkarusEntity& operator=(IkarusEntity&&) = default; + IkarusEntity & operator=(IkarusEntity const &) = default; + IkarusEntity & operator=(IkarusEntity &&) = default; ~IkarusEntity() override = default; }; diff --git a/src/objects/object.hpp b/src/objects/object.hpp index 5a94aa2..c51ddbb 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -10,11 +10,11 @@ struct IkarusObject { public: IkarusObject(struct IkarusProject * project, IkarusId id); - IkarusObject(IkarusObject const&) = default; - IkarusObject(IkarusObject&&) = default; + IkarusObject(IkarusObject const &) = default; + IkarusObject(IkarusObject &&) = default; - IkarusObject& operator=(IkarusObject const&) = default; - IkarusObject& operator=(IkarusObject&&) = default; + IkarusObject & operator=(IkarusObject const &) = default; + IkarusObject & operator=(IkarusObject &&) = default; virtual ~IkarusObject() = default; diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index 2abef0a..bea1a0d 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -14,17 +14,15 @@ IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): IkarusObject{project, id}, _data{data} {} -IkarusProperty::Data& IkarusProperty::get_data() { +IkarusProperty::Data & IkarusProperty::get_data() { return _data; } -IkarusProperty::Data const& IkarusProperty::get_data() const { +IkarusProperty::Data const & IkarusProperty::get_data() const { return _data; } -cppbase::Result IkarusProperty::get_property_type( - IkarusProject * project, IkarusId id -) { +cppbase::Result IkarusProperty::get_property_type(IkarusProject * project, IkarusId id) { LOG_DEBUG("fetching unboxed property type"); LOG_VERBOSE("project={};property={}", project->get_path().c_str(), id); @@ -33,16 +31,14 @@ cppbase::Result IkarusProperty: VTRY( auto const type, - project->get_db() - ->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch unboxed property type: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) + project->get_db()->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id).on_error([ctx](auto const & err) { + ctx->set_error( + fmt::format("failed to fetch unboxed property type: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) ); return cppbase::ok(static_cast(type)); @@ -55,20 +51,14 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { auto * ctx = property->project->get_function_context(); - TRYRV( - , - property->project - ->get_db() - ->execute("DELETE FROM `objects` WHERE `id` = ?", property->id) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to delete property from objects table: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); + TRYRV(, property->project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([ctx](auto const & err) { + ctx->set_error( + fmt::format("failed to delete property from objects table: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + })); LOG_VERBOSE("property was successfully deleted from database, freeing"); @@ -80,8 +70,7 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { LOG_VERBOSE("fetching property type"); - return IkarusProperty::get_property_type(property->project, property->id) - .unwrap_value_or(IkarusPropertyType_Toggle); + return IkarusProperty::get_property_type(property->project, property->id).unwrap_value_or(IkarusPropertyType_Toggle); } IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { @@ -94,10 +83,9 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p VTRYRV( auto const source, nullptr, - property->project - ->get_db() + property->project->get_db() ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) - .on_error([ctx](auto const& err) { + .on_error([ctx](auto const & err) { ctx->set_error( fmt::format("failed to fetch property's source: {}", err), true, @@ -133,10 +121,9 @@ IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) VTRYRV( auto const value, nullptr, - property->project - ->get_db() + property->project->get_db() ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id) - .on_error([ctx](auto const& err) { + .on_error([ctx](auto const & err) { ctx->set_error( fmt::format("failed to fetch property's default value: {}", err), true, diff --git a/src/objects/properties/property.hpp b/src/objects/properties/property.hpp index 6acd377..efdb515 100644 --- a/src/objects/properties/property.hpp +++ b/src/objects/properties/property.hpp @@ -16,24 +16,23 @@ public: public: /// \brief Helper to fetch a type for a property that isn't yet wrapped in an object - [[nodiscard]] static cppbase::Result get_property_type( - struct IkarusProject * project, IkarusId id - ); + [[nodiscard]] static cppbase::Result + get_property_type(struct IkarusProject * project, IkarusId id); public: IkarusProperty(struct IkarusProject * project, IkarusId id, Data data); - IkarusProperty(IkarusProperty const&) = default; - IkarusProperty(IkarusProperty&&) = default; + IkarusProperty(IkarusProperty const &) = default; + IkarusProperty(IkarusProperty &&) = default; - IkarusProperty& operator=(IkarusProperty const&) = default; - IkarusProperty& operator=(IkarusProperty&&) = default; + IkarusProperty & operator=(IkarusProperty const &) = default; + IkarusProperty & operator=(IkarusProperty &&) = default; ~IkarusProperty() override = default; public: - [[nodiscard]] Data& get_data(); - [[nodiscard]] Data const& get_data() const; + [[nodiscard]] Data & get_data(); + [[nodiscard]] Data const & get_data() const; private: Data _data; diff --git a/src/objects/properties/property_source.hpp b/src/objects/properties/property_source.hpp index 0fda4ed..a98d664 100644 --- a/src/objects/properties/property_source.hpp +++ b/src/objects/properties/property_source.hpp @@ -11,11 +11,11 @@ public: public: explicit IkarusPropertySource(Data data); - IkarusPropertySource(IkarusPropertySource const&) = default; - IkarusPropertySource(IkarusPropertySource&&) = default; + IkarusPropertySource(IkarusPropertySource const &) = default; + IkarusPropertySource(IkarusPropertySource &&) = default; - IkarusPropertySource& operator=(IkarusPropertySource const&) = default; - IkarusPropertySource& operator=(IkarusPropertySource&&) = default; + IkarusPropertySource & operator=(IkarusPropertySource const &) = default; + IkarusPropertySource & operator=(IkarusPropertySource &&) = default; virtual ~IkarusPropertySource() = default; diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp index 70ec934..db5105b 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/objects/properties/toggle_property.cpp @@ -3,8 +3,5 @@ IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} -IkarusToggleProperty * ikarus_toggle_property_create( - struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source -) { - -} +IkarusToggleProperty * +ikarus_toggle_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source) {} diff --git a/src/objects/util.hpp b/src/objects/util.hpp index f192b7a..0ba4b37 100644 --- a/src/objects/util.hpp +++ b/src/objects/util.hpp @@ -55,7 +55,7 @@ template return cppbase::ok(id); }) - .on_error([&](auto const& err) { + .on_error([&](auto const & err) { ctx->set_error( fmt::format("unable to insert {} into database: {}", object_type_str, err), true, @@ -81,7 +81,7 @@ void delete_object(IkarusProject * project, Object * object) { auto * ctx = object->project->get_function_context(); - TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const& err) { + TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to delete {} from objects table: {}", object_type_str, err), true, @@ -104,7 +104,7 @@ struct SingleQueryData { template requires std::derived_from -cppbase::Result fetch_single_field(Object const * object, SingleQueryData const& query_data) { +cppbase::Result fetch_single_field(Object const * object, SingleQueryData const & query_data) { auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); LOG_VERBOSE("fetching property default value"); @@ -120,7 +120,7 @@ cppbase::Result fetch_single_field(Object const fmt::format("SELECT `{}` FROM `{}` WHERE `id` = ?", query_data.select_field_name, query_data.table_name), object->id ) - .on_error([&](auto const& err) { + .on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.select_field_name, err), true, @@ -143,10 +143,13 @@ struct MultipleBufferQueryData { template requires std::derived_from void fetch_multiple_buffered( - Object const * object, MultipleBufferQueryData const& query_data, Mapped * mapped_buffer, size_t buffer_size, F transformer + Object const * object, + MultipleBufferQueryData const & query_data, + Mapped * mapped_buffer, + size_t buffer_size, + F transformer ) - requires cppbase:: - is_result_with_value_type_v> + requires cppbase::is_result_with_value_type_v> { auto * ctx = object->project->get_function_context(); @@ -172,7 +175,7 @@ void fetch_multiple_buffered( buffer_size, object->id ) - .on_error([&](auto const& err) { + .on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.relation_desc, err), true, @@ -182,9 +185,7 @@ void fetch_multiple_buffered( }) ); - LOG_DEBUG( - "{} {}: [{}]", object_type_str, query_data.relation_desc, fmt::join(select_buffer, select_buffer + buffer_size, ", ") - ); + LOG_DEBUG("{} {}: [{}]", object_type_str, query_data.relation_desc, fmt::join(select_buffer, select_buffer + buffer_size, ", ")); for (size_t i = 0; i < buffer_size; ++i) { VTRYRV(mapped_buffer[i], , transformer(object->project, ctx, select_buffer[i])); @@ -200,7 +201,7 @@ struct CountQueryData { template requires std::derived_from -cppbase::Result fetch_count(Object const * object, CountQueryData const& query_data) { +cppbase::Result fetch_count(Object const * object, CountQueryData const & query_data) { auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); LOG_VERBOSE("fetching {} {} count", object_type_str, query_data.relation_desc); @@ -221,7 +222,7 @@ cppbase::Result fetch_count(Object const ), object->id ) - .on_error([&](auto const& err) { + .on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to fetch {} {} count: {}", object_type_str, query_data.relation_desc, err), true, @@ -238,4 +239,4 @@ cppbase::Result fetch_count(Object const return cppbase::ok(static_cast(count)); } -} +} // namespace ikarus::util diff --git a/src/persistence/function_context.hpp b/src/persistence/function_context.hpp index 769006b..663ff2d 100644 --- a/src/persistence/function_context.hpp +++ b/src/persistence/function_context.hpp @@ -16,11 +16,11 @@ struct IkarusFunctionContext { public: explicit IkarusFunctionContext(struct IkarusProject * project); - IkarusFunctionContext(IkarusFunctionContext const&) noexcept = default; - IkarusFunctionContext(IkarusFunctionContext&&) noexcept = default; + IkarusFunctionContext(IkarusFunctionContext const &) noexcept = default; + IkarusFunctionContext(IkarusFunctionContext &&) noexcept = default; - auto operator=(IkarusFunctionContext const&) noexcept -> IkarusFunctionContext& = default; - auto operator=(IkarusFunctionContext&&) noexcept -> IkarusFunctionContext& = default; + auto operator=(IkarusFunctionContext const &) noexcept -> IkarusFunctionContext & = default; + auto operator=(IkarusFunctionContext &&) noexcept -> IkarusFunctionContext & = default; ~IkarusFunctionContext(); @@ -40,11 +40,7 @@ public: _project->error_infos = {infos...}; if (log_error) { - LOG_ERROR( - "Error({}): {}", - fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), - error_message - ); + LOG_ERROR("Error({}): {}", fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), error_message); } } diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index bf1e51a..0be9991 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -14,7 +14,7 @@ auto IkarusProject::get_name() const -> std::string_view { return _name; } -auto IkarusProject::get_path() const -> std::filesystem::path const& { +auto IkarusProject::get_path() const -> std::filesystem::path const & { return _path; } @@ -55,8 +55,7 @@ auto IkarusProject::get_property(IkarusId id, IkarusPropertyType type) -> Ikarus return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); case IkarusPropertyType_Number: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); - case IkarusPropertyType_Text: - return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); + case IkarusPropertyType_Text: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); } } diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index 7d9d5b0..2359f2e 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -18,7 +18,7 @@ struct IkarusProject { public: [[nodiscard]] auto get_name() const -> std::string_view; - [[nodiscard]] auto get_path() const -> std::filesystem::path const&; + [[nodiscard]] auto get_path() const -> std::filesystem::path const &; [[nodiscard]] auto get_db() -> sqlitecpp::Connection *; [[nodiscard]] auto get_db() const -> sqlitecpp::Connection const *; @@ -38,7 +38,7 @@ public: private: template - [[nodiscard]] T * get_cached_object(IkarusId id, auto& cache) { + [[nodiscard]] T * get_cached_object(IkarusId id, auto & cache) { auto const iter = cache.find(id); if (iter == cache.cend()) { @@ -49,7 +49,7 @@ private: } template - void remove_cached_object(T * object, std::unordered_map>& cache) { + void remove_cached_object(T * object, std::unordered_map> & cache) { cache.erase(object->id); } diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp index b408925..c339149 100644 --- a/src/values/number_value.cpp +++ b/src/values/number_value.cpp @@ -45,7 +45,7 @@ void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined } char const * ikarus_number_value_to_string(IkarusNumberValue const * value) { - return ikarus_value_base_to_string(value, [](auto const& value) { return value; }); + return ikarus_value_base_to_string(value, [](auto const & value) { return value; }); } bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs) { diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index 1421ca3..8644d3e 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -12,15 +12,14 @@ public: public: explicit IkarusNumberValue(); - IkarusNumberValue(IkarusNumberValue const&) = default; - IkarusNumberValue(IkarusNumberValue&&) = default; + IkarusNumberValue(IkarusNumberValue const &) = default; + IkarusNumberValue(IkarusNumberValue &&) = default; - IkarusNumberValue& operator=(IkarusNumberValue const&) = default; - IkarusNumberValue& operator=(IkarusNumberValue&&) = default; + IkarusNumberValue & operator=(IkarusNumberValue const &) = default; + IkarusNumberValue & operator=(IkarusNumberValue &&) = default; ~IkarusNumberValue() override = default; public: - boost::variant2::variant> data{ - }; + boost::variant2::variant> data{}; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp index ff09770..64006ac 100644 --- a/src/values/text_value.cpp +++ b/src/values/text_value.cpp @@ -46,7 +46,7 @@ void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined) { } char const * ikarus_text_value_to_string(IkarusTextValue const * value) { - return ikarus_value_base_to_string(value, [](auto const& value) { return value; }); + return ikarus_value_base_to_string(value, [](auto const & value) { return value; }); } bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs) { diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 117e175..40d3945 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -11,15 +11,14 @@ public: public: explicit IkarusTextValue(); - IkarusTextValue(IkarusTextValue const&) = default; - IkarusTextValue(IkarusTextValue&&) = default; + IkarusTextValue(IkarusTextValue const &) = default; + IkarusTextValue(IkarusTextValue &&) = default; - IkarusTextValue& operator=(IkarusTextValue const&) = default; - IkarusTextValue& operator=(IkarusTextValue&&) = default; + IkarusTextValue & operator=(IkarusTextValue const &) = default; + IkarusTextValue & operator=(IkarusTextValue &&) = default; ~IkarusTextValue() override = default; public: - boost::variant2::variant> data{ - }; + boost::variant2::variant> data{}; }; diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp index c5ced27..dabbe00 100644 --- a/src/values/toggle_value.cpp +++ b/src/values/toggle_value.cpp @@ -46,7 +46,7 @@ void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined } char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value) { - return ikarus_value_base_to_string(value, [](auto const& value) { return value ? "✓" : "✗"; }); + return ikarus_value_base_to_string(value, [](auto const & value) { return value ? "✓" : "✗"; }); } bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs) { diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index 7e2240f..20a5dd6 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -11,15 +11,14 @@ public: public: explicit IkarusToggleValue(); - IkarusToggleValue(IkarusToggleValue const&) = default; - IkarusToggleValue(IkarusToggleValue&&) = default; + IkarusToggleValue(IkarusToggleValue const &) = default; + IkarusToggleValue(IkarusToggleValue &&) = default; - IkarusToggleValue& operator=(IkarusToggleValue const&) = default; - IkarusToggleValue& operator=(IkarusToggleValue&&) = default; + IkarusToggleValue & operator=(IkarusToggleValue const &) = default; + IkarusToggleValue & operator=(IkarusToggleValue &&) = default; ~IkarusToggleValue() override = default; public: - boost::variant2::variant> data{ - }; + boost::variant2::variant> data{}; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index 2cffcaa..cf9a270 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -19,7 +19,7 @@ IkarusValue::IkarusValue(Data data): data(data) {} -cppbase::Result IkarusValue::from_json(boost::json::value const& json) { +cppbase::Result IkarusValue::from_json(boost::json::value const & json) { if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); } else { @@ -43,8 +43,9 @@ cppbase::Result IkarusValue::from_jso ret = new T{}; ret->data = boost::variant2::monostate{}; } else { - auto res = boost::json::try_value_to< - boost::container::small_vector>(*data); + auto res = + boost::json::try_value_to>(*data + ); if (res.has_error()) { return cppbase::err(FromJsonError{}); @@ -88,8 +89,8 @@ boost::json::value IkarusValue::to_json() const { [](T const * value) -> boost::json::value { return boost::variant2::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); } + []([[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 ); diff --git a/src/values/value.hpp b/src/values/value.hpp index 20b4717..adc27ca 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -13,18 +13,18 @@ public: public: explicit IkarusValue(Data data); - IkarusValue(IkarusValue const&) = default; - IkarusValue(IkarusValue&&) noexcept = default; + IkarusValue(IkarusValue const &) = default; + IkarusValue(IkarusValue &&) noexcept = default; - IkarusValue& operator=(IkarusValue const&) = default; - IkarusValue& operator=(IkarusValue&&) noexcept = default; + IkarusValue & operator=(IkarusValue const &) = default; + IkarusValue & operator=(IkarusValue &&) noexcept = default; virtual ~IkarusValue() = default; public: struct FromJsonError {}; - [[nodiscard]] static cppbase::Result from_json(boost::json::value const& json); + [[nodiscard]] static cppbase::Result from_json(boost::json::value const & json); [[nodiscard]] boost::json::value to_json() const; public: diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp index 46cf457..d4e6e25 100644 --- a/src/values/value_base.hpp +++ b/src/values/value_base.hpp @@ -2,16 +2,14 @@ #include -#include - #include +#include + template typename V::DataType const * ikarus_value_base_get(V * value, size_t idx) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { return &(*data)[idx]; } @@ -22,9 +20,7 @@ typename V::DataType const * ikarus_value_base_get(V * value, size_t idx) { template size_t ikarus_value_base_get_size(V const * value) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { return data->size(); } @@ -35,9 +31,7 @@ size_t ikarus_value_base_get_size(V const * value) { template void ikarus_value_base_set(V * value, size_t idx, typename V::DataType new_data) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { (*data)[idx] = new_data; } @@ -46,9 +40,7 @@ void ikarus_value_base_set(V * value, size_t idx, typename V::DataType new_data) template void ikarus_value_base_remove(V * value, size_t idx) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { data->erase(data->begin() + idx); } @@ -57,9 +49,7 @@ void ikarus_value_base_remove(V * value, size_t idx) { template void ikarus_value_base_insert(V * value, size_t idx, typename V::DataType new_data) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { data->insert(data->begin() + idx, new_data); } @@ -68,9 +58,7 @@ void ikarus_value_base_insert(V * value, size_t idx, typename V::DataType new_da template void ikarus_value_base_clear(V * value) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { data->clear(); } @@ -93,10 +81,9 @@ void ikarus_value_base_set_undefined(V * value, bool undefined) { template F> char const * ikarus_value_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 * { + cppbase::overloaded{ + [](boost::variant2::monostate const &) -> char const * { return nullptr; }, + [&transformer](auto const & data) -> char const * { auto buffer = fmt::memory_buffer{}; fmt::format_to( From 13673738190165a3d20b9d495471a23afc1ac71e Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 28 Dec 2023 23:42:43 +0100 Subject: [PATCH 033/166] remove logging statements Signed-off-by: Folling --- clang-format.txt | 192 ++++++++++++++++++++++++++++ src/objects/blueprint.cpp | 2 +- src/objects/entity.cpp | 40 ++++-- src/objects/properties/property.cpp | 22 ---- src/objects/util.hpp | 77 ++++++----- vendor/sqlitecpp | 2 +- 6 files changed, 260 insertions(+), 75 deletions(-) create mode 100644 clang-format.txt diff --git a/clang-format.txt b/clang-format.txt new file mode 100644 index 0000000..a600edb --- /dev/null +++ b/clang-format.txt @@ -0,0 +1,192 @@ +BasedOnStyle: Google + +AccessModifierOffset: -4 + +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: + Enabled: false +AlignConsecutiveBitFields: + Enabled: false +AlignConsecutiveDeclarations: + Enabled: false +AlignConsecutiveMacros: AcrossEmptyLines +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true + +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true + +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes + +BinPackArguments: false +BinPackParameters: false + +BitFieldColonSpacing: Both + +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyNamespace: false + SplitEmptyRecord: false + +BracedInitializerIndentWidth: 4 + +# BreakAdjacentStringLiterals: true +BreakAfterAttributes: Never +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeConceptDeclarations: Always +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: false + +ColumnLimit: 140 + +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 + +Cpp11BracedListStyle: true + +DerivePointerAlignment: false + +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: Always + +ExperimentalAutoDetectBinPacking: true + +FixNamespaceComments: true + +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^".+\.(h|hpp)"$' + Priority: 1 + - Regex: '^<[a-z0-9_]+\.h>$' + Priority: 2 + - Regex: '^<[a-z0-9_]+>$' + Priority: 3 + - Regex: '^$' + Priority: 4 + - Regex: '^$' + Priority: 5 + - Regex: '^$' + Priority: 6 + - Regex: '^$' + Priority: 7 + - Regex: '^$' + Priority: 8 + - Regex: '^$' + Priority: 9 + - Regex: '^$' + Priority: 10 + - Regex: '^$' + Priority: 11 + - Regex: '^$' + Priority: 12 + - Regex: '^$' + Priority: 13 + +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: NoIndent +IndentGotoLabels: false +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: true +InsertNewlineAtEOF: true +InsertTrailingCommas: Wrapped + +IntegerLiteralSeparator: + Binary: -1 + Decimal: 3 + Hex: -1 + +KeepEmptyLinesAtEOF: false +KeepEmptyLinesAtTheStartOfBlocks: false + +LambdaBodyIndentation: Signature +Language: Cpp + +LineEnding: LF + +MaxEmptyLinesToKeep: 1 + +NamespaceIndentation: None + +PPIndentWidth: -1 +PackConstructorInitializers: Never + +PointerAlignment: Middle +QualifierAlignment: Right +# QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ] +ReferenceAlignment: Middle + +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: MultipleParentheses +RemoveSemicolon: true + +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope + +SeparateDefinitionBlocks: Always + +SortIncludes: CaseInsensitive +SortUsingDeclarations: LexicographicNumeric + +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: Both +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: 1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++20 + +TabWidth: 4 +UseTab: Never diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index c8bf1e5..c15c7bc 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -27,7 +27,7 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c } void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { - ikarus::util::delete_object(blueprint->project, blueprint); + ikarus::util::delete_object(blueprint); } void ikarus_blueprint_get_properties( diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp index ce5d853..80d047e 100644 --- a/src/objects/entity.cpp +++ b/src/objects/entity.cpp @@ -2,20 +2,36 @@ #include +#include +#include #include #include IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name) { - LOG_INFO("creating new entity"); - - LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); - - auto * ctx = project->get_function_context(); - - if (cppbase::is_empty_or_blank(name)) { - ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - // TODO + return ikarus::util::insert_object( + project, + IkarusObjectType_Entity, + name, + [](auto * db, IkarusId id) { return db->execute("INSERT INTO `entities`(`id`) VALUES(?)", id); }, + [project](IkarusId id) { return project->get_entity(id); } + ).unwrap_value_or(nullptr); } + +void ikarus_entity_delete(IkarusEntity * entity) { + ikarus::util::delete_object(entity); +} + +bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint) { + return ikarus::util::check_exists( + entity, + ikarus::util::ExistsQueryData{ + .table_name = "entity_blueprint_links", + .where_field_name = "blueprint", + .where_field_value = blueprint->id, + .relation_desc = "linked blueprints" + } + ) + .unwrap_value_or(false); +} + +bool ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint) {} diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index bea1a0d..d28275d 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -23,10 +23,6 @@ IkarusProperty::Data const & IkarusProperty::get_data() const { } cppbase::Result IkarusProperty::get_property_type(IkarusProject * project, IkarusId id) { - LOG_DEBUG("fetching unboxed property type"); - - LOG_VERBOSE("project={};property={}", project->get_path().c_str(), id); - auto * ctx = project->get_function_context(); VTRY( @@ -45,10 +41,6 @@ cppbase::Result IkarusProperty: } IKA_API void ikarus_property_delete(IkarusProperty * property) { - LOG_INFO("deleting property"); - - LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->project->get_function_context(); TRYRV(, property->project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([ctx](auto const & err) { @@ -60,24 +52,14 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { ); })); - LOG_VERBOSE("property was successfully deleted from database, freeing"); - property->project->uncache(property); - - LOG_VERBOSE("successfully deleted property"); } IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { - LOG_VERBOSE("fetching property type"); - return IkarusProperty::get_property_type(property->project, property->id).unwrap_value_or(IkarusPropertyType_Toggle); } IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { - LOG_VERBOSE("fetching property source"); - - LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->project->get_function_context(); VTRYRV( @@ -112,10 +94,6 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p } IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) { - LOG_VERBOSE("fetching property default value"); - - LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->project->get_function_context(); VTRYRV( diff --git a/src/objects/util.hpp b/src/objects/util.hpp index 0ba4b37..2779478 100644 --- a/src/objects/util.hpp +++ b/src/objects/util.hpp @@ -28,10 +28,6 @@ template ) { auto const * object_type_str = ikarus_object_type_to_string(type); - LOG_INFO("creating new {}", object_type_str); - - LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); - auto * ctx = project->get_function_context(); if (cppbase::is_empty_or_blank(name)) { @@ -43,14 +39,10 @@ template auto const id, project->get_db() ->transact([&](auto * db) -> cppbase::Result { - LOG_VERBOSE("creating {} in objects table", object_type_str); - TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(type), name)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - LOG_DEBUG("{} is {}", object_type_str, id); - TRY(insert_function(db, id)); return cppbase::ok(id); @@ -65,23 +57,17 @@ template }) ); - LOG_VERBOSE("successfully created {}", object_type_str); - return cppbase::ok(object_factory(id)); } template requires std::derived_from -void delete_object(IkarusProject * project, Object * object) { +void delete_object(Object * object) { auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - LOG_INFO("deleting {}", object_type_str); - - LOG_DEBUG("project={}; {}={}", object_type_str, object->project->get_path().c_str(), object->id); - auto * ctx = object->project->get_function_context(); - TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const & err) { + TRYRV(, object->project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to delete {} from objects table: {}", object_type_str, err), true, @@ -90,11 +76,7 @@ void delete_object(IkarusProject * project, Object * object) { ); })); - LOG_VERBOSE("{} was successfully deleted from database, freeing", object_type_str); - - project->uncache(object); - - LOG_VERBOSE("successfully deleted {}", object_type_str); + object->project->uncache(object); } struct SingleQueryData { @@ -107,10 +89,6 @@ template cppbase::Result fetch_single_field(Object const * object, SingleQueryData const & query_data) { auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - LOG_VERBOSE("fetching property default value"); - - LOG_VERBOSE("project={};property={}", object->project->get_path().c_str(), object->id); - auto * ctx = object->project->get_function_context(); VTRY( @@ -155,10 +133,6 @@ void fetch_multiple_buffered( auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - LOG_VERBOSE("fetching {} {}", object_type_str, query_data.relation_desc); - - LOG_VERBOSE("project={};{}={}", object->project->get_path().c_str(), object_type_str, object->id); - Selected select_buffer[buffer_size]; TRYRV( @@ -185,8 +159,6 @@ void fetch_multiple_buffered( }) ); - LOG_DEBUG("{} {}: [{}]", object_type_str, query_data.relation_desc, fmt::join(select_buffer, select_buffer + buffer_size, ", ")); - for (size_t i = 0; i < buffer_size; ++i) { VTRYRV(mapped_buffer[i], , transformer(object->project, ctx, select_buffer[i])); } @@ -204,12 +176,8 @@ template cppbase::Result fetch_count(Object const * object, CountQueryData const & query_data) { auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - LOG_VERBOSE("fetching {} {} count", object_type_str, query_data.relation_desc); - auto * ctx = object->project->get_function_context(); - LOG_DEBUG("{}={}", object_type_str, object->id); - VTRY( auto count, object->project->get_db() @@ -232,11 +200,42 @@ cppbase::Result fetch_count(Object const }) ); - LOG_DEBUG("{} {} count: {}", object_type_str, query_data.relation_desc, count); - - LOG_VERBOSE("successfully fetched {} {} count", object_type_str, query_data.relation_desc); - return cppbase::ok(static_cast(count)); } +template +struct ExistsQueryData { + std::string_view table_name; + std::string_view where_field_name; + T where_field_value; + std::string_view relation_desc; +}; + +template + requires std::derived_from +cppbase::Result check_exists(Object const * object, ExistsQueryData const & query_data) { + auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); + + auto * ctx = object->project->get_function_context(); + + VTRY( + auto exists, + object->project->get_db() + ->template query_one( + fmt::format("SELECT EXISTS(SELECT 1 FROM `{}` WHERE `{}` = ?);", query_data.table_name, query_data.where_field_name), + query_data.where_field_value + ) + .on_error([&](auto const & err) { + ctx->set_error( + fmt::format("failed to check whether {} {} exists: {}", object_type_str, query_data.relation_desc, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + return cppbase::ok(static_cast(exists)); +} + } // namespace ikarus::util diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 2a93b8b..00a1afc 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 2a93b8b1a8be03a9a9c4c72956b1111299de0ddd +Subproject commit 00a1afcc5f564f562c436f1ddfa4f44bb6489b17 From e1bf97704ae53ff31925af2f9434b027c957bb12 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 2 Jan 2024 15:14:39 +0100 Subject: [PATCH 034/166] change error system & function signatures Signed-off-by: Folling --- .clang-format | 2 +- .clang-tidy | 2 +- README.md | 2 +- include/ikarus/errors.h | 142 ++++++++--- include/ikarus/id.h | 6 +- include/ikarus/objects/blueprint.h | 36 ++- include/ikarus/objects/entity.h | 53 ++-- include/ikarus/objects/object.h | 16 +- include/ikarus/objects/object_type.h | 4 +- .../objects/properties/number_property.h | 35 ++- include/ikarus/objects/properties/property.h | 25 +- .../objects/properties/property_source.h | 15 +- .../ikarus/objects/properties/text_property.h | 34 ++- .../objects/properties/toggle_property.h | 33 ++- include/ikarus/persistence/project.h | 96 +++---- include/ikarus/values/number_value.h | 47 ++-- include/ikarus/values/text_value.h | 53 ++-- include/ikarus/values/toggle_value.h | 44 ++-- include/ikarus/values/value.h | 10 +- src/errors.cpp | 81 ++++-- src/objects/blueprint.cpp | 132 +++++----- src/objects/entity.cpp | 1 - src/objects/properties/property.cpp | 46 ++-- src/objects/util.hpp | 241 ------------------ src/persistence/function_context.cpp | 18 -- src/persistence/function_context.hpp | 49 ---- src/persistence/project.cpp | 27 +- src/persistence/project.hpp | 34 +-- 28 files changed, 633 insertions(+), 651 deletions(-) delete mode 100644 src/objects/util.hpp delete mode 100644 src/persistence/function_context.cpp delete mode 100644 src/persistence/function_context.hpp diff --git a/.clang-format b/.clang-format index a600edb..7d8a46d 100644 --- a/.clang-format +++ b/.clang-format @@ -67,7 +67,7 @@ BreakInheritanceList: AfterColon BreakStringLiterals: false ColumnLimit: 140 - +CommentPragmas: '^\\.+' CompactNamespaces: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 diff --git a/.clang-tidy b/.clang-tidy index 7262427..c2cf005 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,7 +1,7 @@ Checks: >- -*, bugprone-*, -bugprone-lambda-function-name, - cppcoreguidelines-*, -cppcoreguidelines-owning-memory, + cppcoreguidelines-*, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes, clang-analyzer-*, google-*, -google-readability-todo, modernize-*, -modernize-use-trailing-return-type, diff --git a/README.md b/README.md index 57b6325..67c46f2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A snapshot if you will. One must not rely on it representing the actual state of the project at any given time. The data is simply copied from the underlying data sources and returned to the caller. -No mechanisms are provided to avoid race conditions. LibIkarus itself should only be used in a single-threaded context. +No mechanisms are provided to avoid race conditions. libikarus itself should only be used in a single-threaded context. However, nothing breaks if you do use it in a multithreaded context, that is, libikarus is threadsafe. You just cannot rely on the data being consistent. This goes especially for inter-process access to the same project. \ No newline at end of file diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index 3218d74..92b16cf 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -7,57 +7,111 @@ /// \addtogroup errors Errors /// \brief Error handling within libikarus -/// \details Errors are stored for each project, akin to the errno handling in C. -/// We store multiple pieces of information about the error occurring. For more information see -/// #ikarus_project_get_error_message. +/// \details Functions in Ikarus may fail, in which case they have an out parameter for the error. +/// Upon erring the function will store relevant information about the error in the out parameter. +/// If the out parameter is null nothing will be stored. This is not recommended as it essentially ignores errors. +/// For the sake of simplicity we have avoided mechanisms that "force" clients to handle errors. +/// Note that Ikarus does not check for null pointers. Passing null pointers to functions that do not explicitly state that they accept null +/// pointers is undefined behaviour. This decision is done for the sake of brevity and readability. `project_get_name(project)` is also +/// synonymous to `project->get_name()` in OOP languages, which shares the same semantics. /// @{ IKARUS_BEGIN_HEADER /// \brief Delineates what caused an error. -/// \details First 2 bytes delineate the major type, next 2 bytes delineate the minor type, next 4 bytes delineate the -/// detail type. /// \remark Note that this doesn't show responsibility. An error with source "SubSystem" could still be the -/// fault of libikarus. +/// fault of libikarus, it just indicates where the error occurred. enum IkarusErrorInfo { /// \brief No error occurred. - IkarusErrorInfo_Source_None = 0x0001000000000000, + IkarusErrorInfo_None = 0x0, /// \brief The error was caused by the client. - IkarusErrorInfo_Source_Client = 0x0001000000000001, - /// \brief The error was caused by a sub-system of libikarus. - IkarusErrorInfo_Source_SubSystem = 0x0001000000000002, + IkarusErrorInfo_Client = 0x10100000, + /// \brief The error was caused by a dependency (e.g. boost) of libikarus. + IkarusErrorInfo_Dependency = 0x10200000, + /// \brief The error was caused by the filesystem. + IkarusErrorInfo_Filesystem = 0x10300000, + /// \brief The error was caused by the database. + IkarusErrorInfo_Database = 0x10400000, + /// \brief The error was caused by the underlying OS. + IkarusErrorInfo_OS = 0x10500000, /// \brief The error was caused by libikarus itself. - IkarusErrorInfo_Source_LibIkarus = 0x0001000000000003, - /// \brief The error was caused by an unknown source. - IkarusErrorInfo_Source_Unknown = 0x00010000FFFFFFFF, - /// \brief No error occurred. - IkarusErrorInfo_Type_None = 0x0002000000000000, - /// \brief The user misused the API. + IkarusErrorInfo_LibIkarus = 0x10600000, + + /// \brief The client misused the API. /// Example: Accessing a resource that does not exist. - IkarusErrorInfo_Type_Client_Misuse = 0x0002000100000001, - /// \brief The user provided invalid input. - /// Example: Passing null for a pointer that must not be null. - IkarusErrorInfo_Type_Client_Input = 0x0002000100000002, - /// \brief An error occurred while interacting with a dependency from ikarus. - /// Example: An error occurred in the underlying OS library. - IkarusErrorInfo_Type_SubSystem_Dependency = 0x0002000200000001, - /// \brief An error occurred while interacting with the database. - /// Example: An error occurred while executing a query. - IkarusErrorInfo_Type_SubSystem_Database = 0x0002000200000002, - /// \brief An error occurred while interacting with the filesystem. - /// Example: An error occurred while reading a file. - IkarusErrorInfo_Type_SubSystem_Filesystem = 0x0002000200000003, + IkarusErrorInfo_Client_Misuse = 0x10100001, + /// \brief The client provided a null value for a parameter that must not be null. + /// Example: Passing null for `ikarus_project_get_name` + IkarusErrorInfo_Client_InvalidNull = 0x10100002, + /// \brief The client provided an index that was out of bounds for some array. + /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3. + IkarusErrorInfo_Client_IndexOutOfBounds = 0x10100003, + /// \brief The client provided a numeric value that was out of bounds + /// Example: Passing the value 2^32 to an i32 (might be passed as a string). + IkarusErrorInfo_Client_ValueOutOfBounds = 0x10100004, + /// \brief The client provided invalid input that doesn't fit in any of the other categories. + /// Example: Passing an empty/blank string for a string that must be non-empty/-blank. + IkarusErrorInfo_Client_InvalidInput = 0x10100005, + /// \brief The client provided valid data in an invalid format. + /// Example: Passing a malformed JSON string. + IkarusErrorInfo_Client_InvalidFormat = 0x10100006, + /// \brief The client violated a constraint. + /// \details This error is most likely caused by endusers. + /// Example: A user tries to set the age of a character to an value outside of their specified range. + IkarusErrorInfo_Client_ConstraintViolated = 0x10100007, + + /// \brief A file was not found. + IkarusErrorInfo_Filesystem_NotFound = 0x10300001, + /// \brief A file or directory already exists. + IkarusErrorInfo_Filesystem_AlreadyExists = 0x10300002, + /// \brief Missing permissions to access a file or directory. + IkarusErrorInfo_Filesystem_MissingPermissions = 0x10300003, + /// \brief Insufficient space to perform an operation. + IkarusErrorInfo_Filesystem_InsufficientSpace = 0x10300004, + /// \brief A path is invalid. + IkarusErrorInfo_Filesystem_InvalidPath = 0x10300005, + + /// \brief A database connection failed. + IkarusErrorInfo_Database_ConnectionFailed = 0x10400001, + /// \brief A database query failed. + IkarusErrorInfo_Database_QueryFailed = 0x10400002, + /// \brief A database migration failed. + IkarusErrorInfo_Database_MigrationFailed = 0x10400003, + /// \brief A database is in an invalid state. This indicates a corrupt project. + /// Example: An entity is linked to a non-existant blueprint. + IkarusErrorInfo_Database_InvalidState = 0x10400004, + + /// \brief A system call failed. + IkarusErrorInfo_OS_SystemCallFailed = 0x10500001, + /// \brief A system call returned an invalid value. + IkarusErrorInfo_OS_InvalidReturnValue = 0x10500002, + /// \brief An OOM error occurred. + IkarusErrorInfo_OS_InsufficientMemory = 0x10500003, + /// \brief A datapoint within ikarus is invalid for the current state of the system. /// Example: The name of an object is found to be invalid UTF8. - IkarusErrorInfo_Type_LibIkarus_InvalidState = 0x0002000300000001, - /// \brief LibIkarus is unable to perform a certain operation that should succeed. - /// Example: Migrating a project fails - IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation = 0x0002000300000002, - /// \brief LibIkarus is unable to perform a certain operation within a given timeframe. + IkarusErrorInfo_LibIkarus_InvalidState = 0x20030001, + /// \brief libikarus is unable to perform a certain operation within a given timeframe. /// Example: A query takes longer than the timeout. - IkarusErrorInfo_Type_LibIkarus_Timeout = 0x0002000300000003, - /// \brief The type of error is unknown. - IkarusErrorInfo_Type_Unknown = 0xFFFFFFFFFFFFFFFF, + IkarusErrorInfo_LibIkarus_Timeout = 0x20030003, +}; + +/// \brief The data limits for an error. +enum IkarusErrorDataLimit { + /// \brief The maximum number of error infos that can be stored in an error. + IkarusErrorDataLimit_MaxErrorInfos = 8, + /// \brief The maximum length of an error message. + IkarusErrorDataLimit_MaxMessageLength = 128, +}; + +/// \brief The data stored for an error +struct IkarusErrorData { + /// \brief The scope of the error. + /// \details This array may at most hold #IkarusErrorDataLimit_MaxErrorInfos elements. + /// The first occurrence of #IkarusErrorInfo_None signifies the end of the array. If this happens at idx x== 0, no error occurred. + IkarusErrorInfo infos[IkarusErrorDataLimit_MaxErrorInfos]; + + char message[IkarusErrorDataLimit_MaxMessageLength]; }; /// \brief Gets the name of an error info. @@ -66,6 +120,20 @@ enum IkarusErrorInfo { /// \remark The returned pointer is valid for the lifetime of the program and must not be freed. IKA_API char const * get_error_info_name(IkarusErrorInfo info); +/// \brief Checks if an error data is a success. +/// \param data The error data to check. +/// \return True if the error data is a success, false otherwise. +IKA_API bool ikarus_error_data_is_success(IkarusErrorData const * data); +/// \brief Checks if an error data is an error. +/// \param data The error data to check. +/// \return True if the error data is an error, false otherwise. +IKA_API bool ikarus_error_data_is_error(IkarusErrorData const * data); +/// \brief Formats the error data in a reasonable but unspecified way. +/// \param data The error data to format. +/// \return The formatted error data. +/// \remark Ownership of the returned pointer is passed to the user and must be freed at their leisure using ikarus_free. +IKA_API char const * ikarus_error_data_pretty_format(IkarusErrorData const * data); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/id.h b/include/ikarus/id.h index fc61b05..ae545a9 100644 --- a/include/ikarus/id.h +++ b/include/ikarus/id.h @@ -28,8 +28,12 @@ IKARUS_BEGIN_HEADER /// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. /// - next 7 bits: #IkarusObjectType /// - last 56 bits: incremented counter generated by the database -using IkarusId = int64_t; +typedef int64_t IkarusId; +/// \brief Creates an id from the given data and type. +/// \param data The data to use for the id. +/// \param type The type to use for the id. +/// \return The created id. IKA_API IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type); /// \brief Fetches the object type of the given id. diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 2163e6f..3ce07ce 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -3,6 +3,7 @@ /// \file blueprint.h /// \author Folling +#include #include #include @@ -24,16 +25,18 @@ struct IkarusBlueprint; /// \param name The name of the blueprint. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param error_out \see errors.h /// \return The created blueprint or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name); +IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Deletes & frees a blueprint. /// \param blueprint The blueprint to delete. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark The blueprint must not be accessed after deletion. -IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); +IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. @@ -42,16 +45,22 @@ IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); /// \param properties_out The buffer to write the properties to. /// \pre \li Must not be null. /// \param properties_out_size The size of the buffer. +/// \param error_out \see errors.h /// \see ikarus_blueprint_get_property_count -IKA_API void -ikarus_blueprint_get_properties(IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size); +IKA_API void ikarus_blueprint_get_properties( + IkarusBlueprint const * blueprint, + struct IkarusProperty ** properties_out, + size_t properties_out_size, + IkarusErrorData * error_out +); /// \brief Gets the number of properties of a blueprint. /// \param blueprint The blueprint to get the number of properties of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint); +IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); /// \brief Gets the entities linked to a blueprint. /// \param blueprint The blueprint to get the linked entities of. @@ -60,27 +69,34 @@ IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * bluep /// \param entities_out The buffer to write the entities to. /// \pre \li Must not be null. /// \param entities_out_size The size of the buffer. +/// \param error_out \see errors.h /// \see ikarus_blueprint_get_linked_entity_count -IKA_API void -ikarus_blueprint_get_linked_entities(IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size); +IKA_API void ikarus_blueprint_get_linked_entities( + IkarusBlueprint const * blueprint, + struct IkarusEntity ** entities_out, + size_t entities_out_size, + IkarusErrorData * error_out +); /// \brief Gets the number of entities linked to a blueprint. /// \param blueprint The blueprint to get the number of linked entities of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The number of linked entities or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint); +IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); /// \brief Casts a blueprint to an object. /// \param blueprint The blueprint to cast. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The blueprint represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint); +IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \see ikarus_blueprint_to_object -IKA_API struct IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint); +IKA_API struct IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 3e38e25..cfc09f3 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -37,24 +38,21 @@ struct IkarusEntity; /// \param project The project the entity is part of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param parent The parent folder of the entity. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the entity in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the entity. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param error_out \see errors.h /// \return The created entity or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name); +IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Deletes an entity. /// \param entity The entity to delete. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark The entity must not be accessed after deletion. -IKA_API void ikarus_entity_delete(IkarusEntity * entity); +IKA_API void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); /// \brief Checks if an entity is linked to a blueprint. /// \param entity The entity to check. @@ -63,8 +61,10 @@ IKA_API void ikarus_entity_delete(IkarusEntity * entity); /// \param blueprint The blueprint to check. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return True if the entity is linked to the blueprint, false otherwise. -IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint); +IKA_API bool +ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint, IkarusErrorData * error_out); /// \brief Links an entity to a blueprint. /// \param entity The entity to link. @@ -73,8 +73,9 @@ IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, s /// \param blueprint The blueprint to link the entity to. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark No-op if the entity is already linked to the blueprint. -IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint); +IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \brief Unlinks an entity from a blueprint. All values of the properties of the blueprint the entity is linked with /// will be deleted. @@ -84,8 +85,9 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru /// \param blueprint The blueprint to unlink the entity from. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark No-op if the entity is not linked to the blueprint. -IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint); +IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \brief Checks if an entity has a specific property. /// \param entity The entity to check. @@ -94,15 +96,17 @@ IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct I /// \param property The property to check. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return True if the entity has the property, false otherwise. -IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property); +IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Gets the number of properties of an entity. /// \param entity The entity to get the number of properties of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity); +IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity, IkarusErrorData * error_out); /// \brief Gets the properties of an entity. /// \param entity The entity to get the properties of. @@ -110,8 +114,14 @@ IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity); /// \pre \li Must exist. /// \param properties_out The buffer to write the properties to. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \param properties_out_size The size of the buffer. -IKA_API void ikarus_entity_get_properties(IkarusEntity const * entity, struct IkarusProperty ** properties_out, size_t properties_out_size); +IKA_API void ikarus_entity_get_properties( + IkarusEntity const * entity, + struct IkarusProperty ** properties_out, + size_t properties_out_size, + IkarusErrorData * error_out +); /// \brief Gets the value of a property of an entity. /// \details If the entity has never set the value of the property, the default value is returned (which may be @@ -122,10 +132,12 @@ IKA_API void ikarus_entity_get_properties(IkarusEntity const * entity, struct Ik /// \param property The property to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The value of the property or null if the entity does not have the property or an error occurs. /// \remark Must be freed using /// #ikarus_free. -IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property); +IKA_API struct IkarusEntityValue * +ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Sets the value of a property of an entity. /// \param entity The entity to set the value of. @@ -138,19 +150,26 @@ IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * /// \pre \li Must not be null. /// \pre \li Must be of the same type as the property. /// \pre \li Must be valid for the property's settings. +/// \param error_out \see errors.h /// \remark If the entity does not have the property, this function fails. -IKA_API void ikarus_entity_set_value(IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value); +IKA_API void ikarus_entity_set_value( + IkarusEntity * entity, + struct IkarusProperty const * property, + struct IkarusValue const * value, + IkarusErrorData * error_out +); /// \brief Casts an entity to an object. /// \param entity The entity to cast. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The entity represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity); +IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity, IkarusErrorData * error_out); /// \see ikarus_entity_to_object -IKA_API struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity); +IKA_API struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index ba754c5..2aa3d0c 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -3,6 +3,7 @@ /// \file object.h /// \author Folling +#include #include /// \defgroup object Objects @@ -20,10 +21,14 @@ struct IkarusObject; /// \brief Compares two objects for equality. /// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the -/// same object. \param lhs The left hand side object. \pre \li Must not be null. \param rhs The right hand side object. +/// same object. +/// \param lhs The left hand side object. /// \pre \li Must not be null. +/// \param rhs The right hand side object. +/// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the objects are equal, false otherwise. -IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs); +IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out); /// \brief Visits an object. Calling the appropriate function for the object's type. /// \param object The object to visit. @@ -34,6 +39,7 @@ IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const /// \param property_folder_visitor The function to call if the object is a property folder. Skipped if null. /// \param entity_folder_visitor The function to call if the object is an entity folder. Skipped if null. /// \param data The data passed to the visitor functions. +/// \param error_out \see errors.h IKA_API void ikarus_object_visit( IkarusObject * object, void (*blueprint_visitor)(struct IkarusBlueprint *, void *), @@ -42,7 +48,8 @@ IKA_API void ikarus_object_visit( void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder *, void *), void (*property_folder_visitor)(struct IkarusPropertyFolder *, void *), void (*entity_folder_visitor)(struct IkarusEntityFolder *, void *), - void * data + void * data, + IkarusErrorData * error_out ); /// \see ikarus_object_visit @@ -54,7 +61,8 @@ IKA_API void ikarus_object_visit_const( void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder const *, void *), void (*property_folder_visitor)(struct IkarusPropertyFolder const *, void *), void (*entity_folder_visitor)(struct IkarusEntityFolder const *, void *), - void * data + void * data, + IkarusErrorData * error_out ); IKARUS_END_HEADER diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 4298a9b..7dd3507 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -3,6 +3,7 @@ /// \file object_type.h /// \author Folling +#include #include /// \addtogroup objects Objects @@ -24,9 +25,10 @@ enum IkarusObjectType { /// \brief Converts an IkarusObjectType to a string. /// \param type The type to convert. +/// \param error_out \see errors.h /// \return The string representation of the type. /// \remark The returned string must not be freed. -char const * ikarus_object_type_to_string(IkarusObjectType type); +char const * ikarus_object_type_to_string(IkarusObjectType type, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 3108201..5d24f87 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -3,24 +3,44 @@ /// \file number_property.h /// \author Folling +#include #include /// \addtogroup properties Properties -/// \brief Number properties store a numeric value. (e.g. "Weight" or "Age") +/// \brief Number properties store a value that can be either true or false. (e.g. "Is the character dead?") /// @{ IKARUS_BEGIN_HEADER struct IkarusNumberProperty; -IKA_API IkarusNumberProperty * -ikarus_number_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); +/// \brief Creates a number property. +/// \param project The project to create the property in. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param property_source The property source to create the property for. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The created property or null if an error occurs. +IKA_API IkarusNumberProperty * ikarus_number_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +); /// \brief Sets the default value for a number property. /// \param property The number property. /// \pre \li Must not be null. /// \pre \li Must exist. -IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(struct IkarusNumberProperty * property); +/// \param error_out \see errors.h +/// \return The default value or null if an error occurs. +IKA_API struct IkarusNumberValue * +ikarus_number_property_get_default_value(struct IkarusNumberProperty * property, IkarusErrorData * error_out); /// \brief Sets the default value for a number property. /// \param property The number property. @@ -29,9 +49,14 @@ IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(stru /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. +/// \param error_out \see errors.h /// \remark Please see \ref property.h "the property documentation" for more information on the interplay between /// default values and other settings. -IKA_API void ikarus_number_property_set_default_value(struct IkarusNumberProperty * property, struct IkarusNumberValue * default_value); +IKA_API void ikarus_number_property_set_default_value( + struct IkarusNumberProperty * property, + struct IkarusNumberValue * default_value, + IkarusErrorData * error_out +); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index ef28ca2..34f2d9a 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -3,6 +3,7 @@ /// \file property.h /// \author Folling +#include #include #include #include @@ -59,32 +60,36 @@ struct IkarusProperty; /// \param property The property to delete. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark The property must not be accessed after deletion. -IKA_API void ikarus_property_delete(IkarusProperty * property); +IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * error_out); /// \brief Gets the type info of a property. /// \param property The property to get the type info of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The type info of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property); +IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Gets the source of a property. /// \param property The property to get the source of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The source of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property); +IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Gets the default value of a property. /// \param property The property to get the type info of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The default value of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusProperty const * property); +IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Visits a property. Calling the appropriate function for the property's type. /// \param property The property to visit. @@ -94,12 +99,14 @@ IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusPrope /// \param number_property_visitor The function to call if the property is a number property. Skipped if null. /// \param text_property_visitor The function to call if the property is a text property. Skipped if null. /// \param data The data to pass to the functions. +/// \param error_out \see errors.h IKA_API void ikarus_property_visit( IkarusProperty * property, void (*toggle_property_visitor)(struct IkarusToggleProperty *, void *), void (*number_property_visitor)(struct IkarusNumberProperty *, void *), void (*text_property_visitor)(struct IkarusTextProperty *, void *), - void * data + void * data, + IkarusErrorData * error_out ); /// \see ikarus_property_visit @@ -108,19 +115,21 @@ IKA_API void ikarus_property_visit_const( void (*toggle_property_visitor)(struct IkarusToggleProperty const *, void *), void (*number_property_visitor)(struct IkarusNumberProperty const *, void *), void (*text_property_visitor)(struct IkarusTextProperty const *, void *), - void * data + void * data, + IkarusErrorData * error_out ); /// \brief Casts a property to an object. /// \param property The property to cast. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The property represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty * property); +IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty * property, IkarusErrorData * error_out); /// \see ikarus_property_to_object -IKA_API struct IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property); +IKA_API struct IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/property_source.h b/include/ikarus/objects/properties/property_source.h index 275baa4..f1da29d 100644 --- a/include/ikarus/objects/properties/property_source.h +++ b/include/ikarus/objects/properties/property_source.h @@ -3,6 +3,7 @@ /// \file property_source.h /// \author Folling +#include #include /// \addtogroup properties Properties @@ -16,17 +17,20 @@ struct IkarusPropertySource; /// \param blueprint The blueprint to create the property source for. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint); +IKA_API struct IkarusPropertySource * +ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \brief Creates an entity property source. /// \param entity The entity to create the property source for. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity); +IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity, IkarusErrorData * error_out); /// \brief Visits a property source, calling the appropriate callback. /// \param property_source The property source to visit. @@ -35,11 +39,13 @@ IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struc /// \param blueprint_visitor The callback to call if the source is a blueprint, skipped if null. /// \param entity_visitor The callback to call if the source is an entity, skipped if null. /// \param user_data User data to pass to the callbacks. +/// \param error_out \see errors.h IKA_API void ikarus_property_source_visit( struct IkarusPropertySource * property_source, void (*blueprint_visitor)(struct IkarusBlueprint *, void *), void (*entity_visitor)(struct IkarusEntity *, void *), - void * user_data + void * user_data, + IkarusErrorData * error_out ); /// \see ikarus_property_source_visit @@ -47,7 +53,8 @@ IKA_API void ikarus_property_source_visit_const( struct IkarusPropertySource const * property_source, void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), void (*entity_visitor)(struct IkarusEntity const *, void *), - void * user_data + void * user_data, + IkarusErrorData * error_out ); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index 9c91c80..0d240b2 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -3,24 +3,43 @@ /// \file text_property.h /// \author Folling +#include #include /// \addtogroup properties Properties -/// \brief Text properties store an arbitrary piece of text. (e.g. "Firstname" or "Description") +/// \brief Text properties store a value that can be either true or false. (e.g. "Is the character dead?") /// @{ IKARUS_BEGIN_HEADER struct IkarusTextProperty; -IKA_API IkarusTextProperty * -ikarus_text_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); +/// \brief Creates a text property. +/// \param project The project to create the property in. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param property_source The property source to create the property for. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The created property or null if an error occurs. +IKA_API IkarusTextProperty * ikarus_text_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +); /// \brief Sets the default value for a text property. /// \param property The text property. /// \pre \li Must not be null. /// \pre \li Must exist. -IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property); +/// \param error_out \see errors.h +/// \return The default value or null if an error occurs. +IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property, IkarusErrorData * error_out); /// \brief Sets the default value for a text property. /// \param property The text property. @@ -29,9 +48,14 @@ IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct I /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. +/// \param error_out \see errors.h /// \remark Please see \ref property.h "the property documentation" for more information on the interplay between /// default values and other settings. -IKA_API void ikarus_text_property_set_default_value(struct IkarusTextProperty * property, struct IkarusTextValue * default_value); +IKA_API void ikarus_text_property_set_default_value( + struct IkarusTextProperty * property, + struct IkarusTextValue * default_value, + IkarusErrorData * error_out +); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index cfa51e3..e084123 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -3,6 +3,7 @@ /// \file toggle_property.h /// \author Folling +#include #include /// \addtogroup properties Properties @@ -13,14 +14,33 @@ IKARUS_BEGIN_HEADER struct IkarusToggleProperty; -IKA_API IkarusToggleProperty * -ikarus_toggle_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); +/// \brief Creates a toggle property. +/// \param project The project to create the property in. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param property_source The property source to create the property for. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The created property or null if an error occurs. +IKA_API IkarusToggleProperty * ikarus_toggle_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +); /// \brief Sets the default value for a toggle property. /// \param property The toggle property. /// \pre \li Must not be null. /// \pre \li Must exist. -IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property); +/// \param error_out \see errors.h +/// \return The default value or null if an error occurs. +IKA_API struct IkarusToggleValue * +ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, IkarusErrorData * error_out); /// \brief Sets the default value for a toggle property. /// \param property The toggle property. @@ -29,9 +49,14 @@ IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(stru /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. +/// \param error_out \see errors.h /// \remark Please see \ref property.h "the property documentation" for more information on the interplay between /// default values and other settings. -IKA_API void ikarus_toggle_property_set_default_value(struct IkarusToggleProperty * property, struct IkarusToggleValue * default_value); +IKA_API void ikarus_toggle_property_set_default_value( + struct IkarusToggleProperty * property, + struct IkarusToggleValue * default_value, + IkarusErrorData * error_out +); IKARUS_END_HEADER diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 7df0e45..93c4e11 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -25,28 +25,31 @@ struct IkarusProject; /// \param name The name of the project. Must neither be null nor empty. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param error_out \see errors.h /// \return The created project or null if an error occurs. /// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_create(char const * path, char const * name); +IKA_API IkarusProject * ikarus_project_create(char const * path, char const * name, IkarusErrorData * error_out); /// \brief Creates a project in memory. /// \param name The name of the project. Must neither be null nor empty. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param error_out \see errors.h /// \return The created project or null if an error occurs. /// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name); +IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out); /// \brief Opens an existing project. /// \param path The path to the project. /// \pre \li Must not be null. /// \pre \li Must point to an existing project on the system. +/// \param error_out \see errors.h /// \return The opened project or null if an error occurs. /// \remark Must be freed using ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_open(char const * path); +IKA_API IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_out); /// \brief Copies a project to a new location. /// \details The new project is not opened. @@ -59,25 +62,29 @@ IKA_API IkarusProject * ikarus_project_open(char const * path); /// \param target_name The name of the new project. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param error_out \see errors.h /// \remark If successful the project connection remains intact. The previous location will still exist. -IKA_API void ikarus_project_copy(IkarusProject const * project, char const * target_path, char const * target_name); +IKA_API void +ikarus_project_copy(IkarusProject const * project, char const * target_path, char const * target_name, IkarusErrorData * error_out); /// \brief Deletes a project and all its associated data from the filesystem. /// \param project The project to delete. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark also frees the project. /// \remark In-Memory projects will just be freed. /// \remark If deletion fails, the project pointer remains intact. -IKA_API void ikarus_project_delete(IkarusProject * project); +IKA_API void ikarus_project_delete(IkarusProject * project, IkarusErrorData * error_out); /// \brief Gets the name of a project. /// \param project The project to get the name of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The name of the project. /// \remark Must be freed using #ikarus_free. -IKA_API char const * ikarus_project_get_name(IkarusProject const * project); +IKA_API char const * ikarus_project_get_name(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Sets the name of a project. /// \param project The project to set the name of. @@ -86,15 +93,17 @@ IKA_API char const * ikarus_project_get_name(IkarusProject const * project); /// \param new_name The new name of the project. /// \pre \li Must not be null. /// \pre \li Must not be empty. -IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_name); +/// \param error_out \see errors.h +IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_name, IkarusErrorData * error_out); /// \brief Gets the path of a project. /// \param project The project to get the path of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The path of the project. /// \remark Must be freed using #ikarus_free. -IKA_API char const * ikarus_project_get_path(IkarusProject const * project); +IKA_API char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Moves a project to a new location. /// \param project The project to move. @@ -103,39 +112,19 @@ IKA_API char const * ikarus_project_get_path(IkarusProject const * project); /// \param target_path The new location of the project. /// \pre \li Must not be null. /// \pre \li Must point to a valid unused path on the system. +/// \param error_out \see errors.h /// \remark If successful the project connection remains intact. The previous location will not exist anymore. /// \remark Due to the nature of filesystems this function may not be atomic. -IKA_API void ikarus_project_move(IkarusProject * project, char const * target_path); - -/// \brief Gets the error code of a project. -/// \param project The project to get the error code of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The error code of the project. -IKA_API int ikarus_project_get_error_code(IkarusProject const * project); -/// \brief Gets the error message of a project. -/// \param project The project to get the error message of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The error message of the project. -/// \remark The returned pointer is valid until the project is freed but may be altered by other operations. -/// \warning Must not be freed. -IKA_API char const * ikarus_project_get_error_message(IkarusProject const * project); +IKA_API void ikarus_project_move(IkarusProject * project, char const * target_path, IkarusErrorData * error_out); /// \brief Gets the blueprint root folder of a project. /// \param project The project to get the blueprint root folder of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The blueprint root folder of the project or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder(IkarusProject const * project); - -/// \brief Gets the number of blueprints of a project. -/// \param project The project to get the number of blueprints of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project); +IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Gets the blueprints of a project. /// \param project The project to get the blueprints of. @@ -144,23 +133,30 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project) /// \param blueprints_out The buffer to write the blueprints to. /// \pre \li Must not be null. /// \param blueprints_out_size The size of the buffer. -IKA_API void -ikarus_project_get_blueprints(IkarusProject const * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size); +/// \param error_out \see errors.h +IKA_API void ikarus_project_get_blueprints( + IkarusProject const * project, + struct IkarusBlueprint ** blueprints_out, + size_t blueprints_out_size, + IkarusErrorData * error_out +); + +/// \brief Gets the number of blueprints of a project. +/// \param project The project to get the number of blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The number of blueprints or undefined if an error occurs. +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Gets the entity root folder of a project. /// \param project The project to get the entity root folder of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The entity root folder of the project or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(IkarusProject const * project); - -/// \brief Gets the number of entities of a project. -/// \param project The project to get the number of entities of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of entities or undefined if an error occurs. -IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project); +IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Gets the entities of a project. /// \param project The project to get the entities of. @@ -169,7 +165,21 @@ IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project); /// \param entities_out The buffer to write the entities to. /// \pre \li Must not be null. /// \param entities_out_size The size of the buffer. -IKA_API void ikarus_project_get_entities(IkarusProject const * project, struct IkarusEntity ** entities_out, size_t entities_out_size); +/// \param error_out \see errors.h +IKA_API void ikarus_project_get_entities( + IkarusProject const * project, + struct IkarusEntity ** entities_out, + size_t entities_out_size, + IkarusErrorData * error_out +); + +/// \brief Gets the number of entities of a project. +/// \param project The project to get the number of entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The number of entities or undefined if an error occurs. +IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index cedd61d..1425fed 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -3,8 +3,8 @@ /// \file number_value.h /// \author Folling +#include #include -#include /// \addtogroup values Values /// @{ @@ -14,24 +14,27 @@ IKARUS_BEGIN_HEADER /// \brief A numeric value. For example "Age" or "Height". struct IkarusNumberValue; -/// \brief Creates a number value. +/// \brief Creates an empty number value. +/// \param error_out \see errors.h /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create(); +IKA_API IkarusNumberValue * ikarus_number_value_create(IkarusErrorData * error_out); /// \brief Fetches the underlying data of a number value at a specific index. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. -/// \return The underlying data or null if an error occurs or the value is undefined. -IKA_API double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx); +/// \param error_out \see errors.h +/// \return The underlying data or NaN if an error occurs or the value is undefined. +IKA_API double ikarus_number_value_get(IkarusNumberValue const * value, size_t idx, IkarusErrorData * error_out); /// \brief Fetches the size of the underlying data of a number value. /// \param value The number value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The size of the underlying data or 0 if an error occurs or the value is undefined. -IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); +IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Sets the data of a number value at a specific index. /// \param value The number value. @@ -39,14 +42,16 @@ IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); /// \param idx The index of the data to set. /// \pre \li Must be less than the size of the value. /// \param new_data The new data. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double const * new_data); +/// \param error_out \see errors.h +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double new_data, IkarusErrorData * error_out); /// \brief Removes a data from a number value. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to remove. /// \pre \li Must be less than the size of the value. -IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); +/// \param error_out \see errors.h +IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx, IkarusErrorData * error_out); /// \brief Inserts a data into a number value. /// \param value The number value. @@ -54,60 +59,68 @@ IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. /// \param new_data The new data. -IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, double const * new_data); +/// \param error_out \see errors.h +IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, double new_data, IkarusErrorData * error_out); /// \brief Clears a number value. /// \param value The number value. +/// \param error_out \see errors.h /// \remark Noop if the value is undefined. -IKA_API void ikarus_number_value_clear(IkarusNumberValue * value); +IKA_API void ikarus_number_value_clear(IkarusNumberValue * value, IkarusErrorData * error_out); /// \brief Checks if a number value is undefined. /// \param value The number value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_number_value_is_undefined(IkarusNumberValue const * value); +IKA_API bool ikarus_number_value_is_undefined(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Changes a number value's undefined state. /// \param value The number value. /// \pre \li Must not be null. /// \param undefined The new undefined state. +/// \param error_out \see errors.h /// \remark Noop if the value is already undefined. /// \remark If the value is set to undefined, all data will be cleared. /// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined); +IKA_API void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined, IkarusErrorData * error_out); /// \brief Converts a number value to a string. /// \param value The number value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. /// \remark Undefined if the value is undefined. -IKA_API char const * ikarus_number_value_to_string(IkarusNumberValue const * value); +IKA_API char const * ikarus_number_value_to_string(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. /// \param lhs The left hand side value. /// \pre \li Must not be null. /// \param rhs The right hand side value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the values' data are equal, false otherwise. -IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs); +IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs, IkarusErrorData * error_out); /// \brief Creates a copy of a number value. /// \param value The value to copy. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The copied value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value); +IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Converts a number value to an entity value. /// \param value The number value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value); +IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value, IkarusErrorData * error_out); /// \see ikarus_toggle_value_to_value -IKA_API struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value); +IKA_API struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index b40a7f6..6511eb5 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -3,6 +3,7 @@ /// \file text_value.h /// \author Folling +#include #include #include @@ -14,101 +15,115 @@ IKARUS_BEGIN_HEADER /// \brief A textual value. For example "Surname" or "Description" struct IkarusTextValue; -/// \brief Creates a text value. +/// \brief Creates an empty text value. +/// \param error_out \see errors.h /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create(); +IKA_API IkarusTextValue * ikarus_text_value_create(IkarusErrorData * error_out); /// \brief Fetches the underlying data of a number value at a specific index. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. +/// \param error_out \see errors.h /// \return The underlying data or null if an error occurs or the value is undefined. -/// \remark This value is owned by LibIkarus and must not be freed. -IKA_API char const * ikarus_text_value_get(IkarusTextValue * value, size_t idx); +IKA_API char const * const * ikarus_text_value_get(IkarusTextValue const * value, size_t idx, IkarusErrorData * error_out); /// \brief Fetches the size of the underlying data of a text value. /// \param value The text value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The size of the underlying data or 0 if an error occurs or the value is undefined. -IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value); +IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Sets the data of a text value at a specific index. /// \param value The text value. /// \pre \li Must not be null. /// \param idx The index of the data to set. /// \pre \li Must be less than the size of the value. -/// \param new_data The new data. LibIkarus assumes ownership of this pointer. -IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data); +/// \param new_data The new data. Ownership remains with the caller. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data, IkarusErrorData * error_out); /// \brief Removes a data from a text value. /// \param value The text value. /// \pre \li Must not be null. /// \param idx The index of the data to remove. /// \pre \li Must be less than the size of the value. -IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx); +/// \param error_out \see errors.h +IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx, IkarusErrorData * error_out); /// \brief Inserts a data into a text value. /// \param value The text value. /// \pre \li Must not be null. /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. -/// \param new_data The new data. -IKA_API void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data); +/// \param new_data The new data. Ownership remains with the caller. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +IKA_API void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data, IkarusErrorData * error_out); /// \brief Clears a text value. /// \param value The text value. +/// \param error_out \see errors.h /// \remark Noop if the value is undefined. -IKA_API void ikarus_text_value_clear(IkarusTextValue * value); +IKA_API void ikarus_text_value_clear(IkarusTextValue * value, IkarusErrorData * error_out); /// \brief Checks if a text value is undefined. /// \param value The text value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_text_value_is_undefined(IkarusTextValue const * value); +IKA_API bool ikarus_text_value_is_undefined(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Changes a text value's undefined state. /// \param value The text value. /// \pre \li Must not be null. /// \param undefined The new undefined state. +/// \param error_out \see errors.h /// \remark Noop if the value is already undefined. /// \remark If the value is set to undefined, all data will be cleared. /// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined); +IKA_API void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined, IkarusErrorData * error_out); /// \brief Converts a text value to a string. /// \param value The text value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. /// \remark Undefined if the value is undefined. -IKA_API char const * ikarus_text_value_to_string(IkarusTextValue const * value); +IKA_API char const * ikarus_text_value_to_string(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. /// \param lhs The left hand side value. /// \pre \li Must not be null. /// \param rhs The right hand side value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the values' data are equal, false otherwise. -IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs); +IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs, IkarusErrorData * error_out); /// \brief Creates a copy of a text value. /// \param value The value to copy. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The copied value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value); +IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Converts a text value to an entity value. /// \param value The text value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value); +IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value, IkarusErrorData * error_out); -/// \see ikarus_toggle_value_to_value -IKA_API struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value); +/// \see ikarus_text_value_to_value +IKA_API struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index 2bb84c7..9e1615b 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -3,6 +3,7 @@ /// \file toggle_value.h /// \author Folling +#include #include #include @@ -14,24 +15,27 @@ IKARUS_BEGIN_HEADER /// \brief A true/false boolean-esque value. For example "Is Dead". struct IkarusToggleValue; -/// \brief Creates a toggle. +/// \brief Creates an empty toggle value. +/// \param error_out \see errors.h /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create(); +IKA_API IkarusToggleValue * ikarus_toggle_value_create(IkarusErrorData * error_out); /// \brief Fetches the underlying data of a number value at a specific index. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. +/// \param error_out \see errors.h /// \return The underlying data or null if an error occurs or the value is undefined. -IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue * value, size_t idx); +IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue const * value, size_t idx, IkarusErrorData * error_out); /// \brief Fetches the size of the underlying data of a toggle value. /// \param value The toggle value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The size of the underlying data or 0 if an error occurs or the value is undefined. -IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value); +IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Sets the data of a toggle value at a specific index. /// \param value The toggle value. @@ -39,14 +43,16 @@ IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value); /// \param idx The index of the data to set. /// \pre \li Must be less than the size of the value. /// \param new_data The new data. -IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data); +/// \param error_out \see errors.h +IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data, IkarusErrorData * error_out); /// \brief Removes a data from a toggle value. /// \param value The toggle value. /// \pre \li Must not be null. /// \param idx The index of the data to remove. /// \pre \li Must be less than the size of the value. -IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx); +/// \param error_out \see errors.h +IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx, IkarusErrorData * error_out); /// \brief Inserts a data into a toggle value. /// \param value The toggle value. @@ -54,60 +60,68 @@ IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx); /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. /// \param new_data The new data. -IKA_API void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data); +/// \param error_out \see errors.h +IKA_API void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data, IkarusErrorData * error_out); /// \brief Clears a toggle value. /// \param value The toggle value. +/// \param error_out \see errors.h /// \remark Noop if the value is undefined. -IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value); +IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value, IkarusErrorData * error_out); /// \brief Checks if a toggle value is undefined. /// \param value The toggle value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value); +IKA_API bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Changes a toggle value's undefined state. /// \param value The toggle value. /// \pre \li Must not be null. /// \param undefined The new undefined state. +/// \param error_out \see errors.h /// \remark Noop if the value is already undefined. /// \remark If the value is set to undefined, all data will be cleared. /// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined); +IKA_API void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined, IkarusErrorData * error_out); /// \brief Converts a toggle value to a string. /// \param value The toggle value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. /// \remark Undefined if the value is undefined. -IKA_API char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value); +IKA_API char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. /// \param lhs The left hand side value. /// \pre \li Must not be null. /// \param rhs The right hand side value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the values' data are equal, false otherwise. -IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs); +IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs, IkarusErrorData * error_out); /// \brief Creates a copy of a toggle value. /// \param value The value to copy. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The copied value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value); +IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Converts a toggle value to an entity value. /// \param value The toggle value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value); +IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value, IkarusErrorData * error_out); /// \see ikarus_toggle_value_to_value -IKA_API struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value); +IKA_API struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index 880c2eb..3fcaf63 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -3,6 +3,7 @@ /// \file value.h /// \author Folling +#include #include /// \defgroup values Values @@ -11,7 +12,7 @@ /// These value classes represent plain objects. They are not associated with any entity. /// Each value may be undefined. \see IkarusProperty /// Values are stored as lists. If a property is "singular" then its value is a list of size 1. -/// Values are typed, with types existing for each of the corresponding property types. +/// Values are typed, with types existing for each of the corresponding property types. The data of values starts with the index 0. /// When setting values for a property the type must match the property type and the value must be valid under the /// property's settings. \see PropertyType /// @{ @@ -28,12 +29,14 @@ struct IkarusValue; /// \param number_visitor The function to call if the value is a number value. Skipped if null. /// \param text_visitor The function to call if the value is a text value. Skipped if null. /// \param data The data passed to the visitor functions. +/// \param error_out \see errors.h IKA_API void ikarus_value_visit( IkarusValue * value, void (*toggle_visitor)(struct IkarusToggleValue *, void *), void (*number_visitor)(struct IkarusNumberValue *, void *), void (*text_visitor)(struct IkarusTextValue *, void *), - void * data + void * data, + IkarusErrorData * error_out ); /// \see ikarus_value_visit @@ -42,7 +45,8 @@ IKA_API void ikarus_value_visit_const( void (*toggle_visitor)(struct IkarusToggleValue const *, void *), void (*number_visitor)(struct IkarusNumberValue const *, void *), void (*text_visitor)(struct IkarusTextValue const *, void *), - void * data + void * data, + IkarusErrorData * error_out ); IKARUS_END_HEADER diff --git a/src/errors.cpp b/src/errors.cpp index 54681f2..32b2c99 100644 --- a/src/errors.cpp +++ b/src/errors.cpp @@ -1,22 +1,71 @@ #include "ikarus/errors.h" +#include "cppbase/functional.hpp" + +#include + +#include + +#include + char const * get_error_info_name(IkarusErrorInfo info) { switch (info) { - case IkarusErrorInfo_Source_None: return "IkarusErrorInfo_Source_None"; - case IkarusErrorInfo_Source_Client: return "IkarusErrorInfo_Source_Client"; - case IkarusErrorInfo_Source_SubSystem: return "IkarusErrorInfo_Source_SubSystem"; - case IkarusErrorInfo_Source_LibIkarus: return "IkarusErrorInfo_Source_LibIkarus"; - case IkarusErrorInfo_Source_Unknown: return "IkarusErrorInfo_Source_Unknown"; - case IkarusErrorInfo_Type_None: return "IkarusErrorInfo_Type_None"; - case IkarusErrorInfo_Type_Client_Misuse: return "IkarusErrorInfo_Type_Client_Misuse"; - case IkarusErrorInfo_Type_Client_Input: return "IkarusErrorInfo_Type_Client_Input"; - case IkarusErrorInfo_Type_SubSystem_Dependency: return "IkarusErrorInfo_Type_SubSystem_Dependency"; - case IkarusErrorInfo_Type_SubSystem_Database: return "IkarusErrorInfo_Type_SubSystem_Database"; - case IkarusErrorInfo_Type_SubSystem_Filesystem: return "IkarusErrorInfo_Type_SubSystem_Filesystem"; - case IkarusErrorInfo_Type_LibIkarus_InvalidState: return "IkarusErrorInfo_Type_LibIkarus_InvalidState"; - case IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation: return "IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation"; - case IkarusErrorInfo_Type_LibIkarus_Timeout: return "IkarusErrorInfo_Type_LibIkarus_Timeout"; - case IkarusErrorInfo_Type_Unknown: return "IkarusErrorInfo_Type_Unknown"; - default: return "Unknown"; + case IkarusErrorInfo_None: return "None"; + case IkarusErrorInfo_Client: return "Client"; + case IkarusErrorInfo_Dependency: return "Dependency"; + case IkarusErrorInfo_Filesystem: return "Filesystem"; + case IkarusErrorInfo_Database: return "Database"; + case IkarusErrorInfo_OS: return "OS"; + case IkarusErrorInfo_LibIkarus: return "libikarus"; + + case IkarusErrorInfo_Client_Misuse: return "Misuse"; + case IkarusErrorInfo_Client_InvalidInput: return "InvalidInput"; + case IkarusErrorInfo_Client_InvalidFormat: return "InvalidFormat"; + case IkarusErrorInfo_Client_ConstraintViolated: return "ConstraintViolated"; + + case IkarusErrorInfo_Filesystem_NotFound: return "NotFound"; + case IkarusErrorInfo_Filesystem_AlreadyExists: return "AlreadyExists"; + case IkarusErrorInfo_Filesystem_MissingPermissions: return "MissingPermissions"; + case IkarusErrorInfo_Filesystem_InsufficientSpace: return "InsufficientSpace"; + case IkarusErrorInfo_Filesystem_InvalidPath: return "InvalidPath"; + + case IkarusErrorInfo_Database_ConnectionFailed: return "ConnectionFailed"; + case IkarusErrorInfo_Database_QueryFailed: return "QueryFailed"; + case IkarusErrorInfo_Database_MigrationFailed: return "MigrationFailed"; + case IkarusErrorInfo_Database_InvalidState: return "InvalidState"; + + case IkarusErrorInfo_OS_SystemCallFailed: return "SystemCallFailed"; + case IkarusErrorInfo_OS_InvalidReturnValue: return "InvalidReturnValue"; + case IkarusErrorInfo_OS_InsufficientMemory: return "InsufficientMemory"; + + case IkarusErrorInfo_LibIkarus_InvalidState: return "InvalidState"; + case IkarusErrorInfo_LibIkarus_Timeout: return "Timeout"; + + default: return "Invalid"; } } + +bool ikarus_error_data_is_success(IkarusErrorData const * data) { + return data->infos[0] == IkarusErrorInfo_None; +} + +bool ikarus_error_data_is_error(IkarusErrorData const * data) { + return data->infos[0] != IkarusErrorInfo_None; +} + +char const * ikarus_error_data_pretty_format(IkarusErrorData const * data) { + if (ikarus_error_data_is_success(data)) { + return "Success"; + } + + auto const formatted = fmt::format( + "{} - {}", + fmt::join( + data->infos | std::views::take_while(cppbase::pred_ne(IkarusErrorInfo_None)) | std::views::transform(get_error_info_name), + "->" + ), + data->message + ); + + return strndup(formatted.data(), formatted.size()); +} diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index c15c7bc..117cbfc 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -1,7 +1,6 @@ #include "ikarus/objects/blueprint.h" #include "objects/blueprint.hpp" -#include "util.hpp" #include #include @@ -9,25 +8,44 @@ #include #include -#include -#include #include IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusObject{project, id} {} IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { - return ikarus::util::insert_object( - project, - IkarusObjectType_Blueprint, - name, - [](auto * db, IkarusId id) { return db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id); }, - [project](IkarusId id) { return project->get_blueprint(id); } - ).unwrap_value_or(nullptr); + if (cppbase::is_empty_or_blank(name)) { + project->set_error("blueprint name must not be empty", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + + return nullptr; + } + + project->db + ->transact([name](auto * db) { + TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Blueprint, name)); + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); + TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id)); + return cppbase::ok(); + }) + .on_error([project](auto const & err) { + project->set_error( + fmt::format("failed to create blueprint: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }); } void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { - ikarus::util::delete_object(blueprint); + blueprint->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id).on_error([blueprint](auto const & err) { + blueprint->project->set_error( + fmt::format("failed to delete blueprint from objects table: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }); } void ikarus_blueprint_get_properties( @@ -35,41 +53,34 @@ void ikarus_blueprint_get_properties( struct IkarusProperty ** properties_out, size_t properties_out_size ) { - ikarus::util::fetch_multiple_buffered( - blueprint, - ikarus::util::MultipleBufferQueryData{ - .table_name = "properties", - .select_field_name = "id", - .where_field_name = "blueprint", - .relation_desc = "properties" - }, - properties_out, - properties_out_size, - [&](IkarusProject * project, IkarusFunctionContext * ctx, IkarusId id) -> cppbase::Result { - VTRY(auto const type, IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const & err) { - ctx->set_error( - fmt::format("failed to fetch property {}'s type: {}", id, err), + IkarusId ids[properties_out_size]; + + TRYRV( + , + blueprint->project->db + ->query_many_buffered("SELECT `id` FROM `properties` WHERE `source` = ?", ids, properties_out_size, blueprint->id) + .on_error([&](auto const & err) { + blueprint->project->set_error( + fmt::format("failed to fetch blueprint properties from database: {}", err), true, IkarusErrorInfo_Source_SubSystem, IkarusErrorInfo_Type_SubSystem_Database ); - })); - - return cppbase::ok(project->get_property(id, type)); - } + }) ); + + // not atomic, could be switched to two loops if necessary + for (size_t i = 0; i < properties_out_size; ++i) { + IkarusId id = ids[i]; + + VTRYRV(auto const type, , IkarusProperty::get_property_type(blueprint->project, id)); + + properties_out[i] = blueprint->project->get_property(id, type); + } } size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { - return ikarus::util::fetch_count( - blueprint, - ikarus::util::CountQueryData{ - .table_name = "blueprint_properties", - .select_field_name = "property", - .where_field_name = "blueprint", - .relation_desc = "properties" - } - ) + return blueprint->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", blueprint->id) .unwrap_value_or(0); } @@ -78,31 +89,34 @@ void ikarus_blueprint_get_linked_entities( struct IkarusEntity ** entities_out, size_t entities_out_size ) { - ikarus::util::fetch_multiple_buffered( - blueprint, - ikarus::util::MultipleBufferQueryData{ - .table_name = "entity_blueprint_links", - .select_field_name = "entity", - .where_field_name = "blueprint", - .relation_desc = "linked entities" - }, - entities_out, - entities_out_size, - [&](IkarusProject * project, [[maybe_unused]] IkarusFunctionContext * ctx, IkarusId id - ) -> cppbase::Result { return cppbase::ok(project->get_entity(id)); } + IkarusId ids[entities_out_size]; + + TRYRV( + , + blueprint->project->db + ->query_many_buffered( + "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", + ids, + entities_out_size, + blueprint->id + ) + .on_error([&](auto const & err) { + blueprint->project->set_error( + fmt::format("failed to fetch linked entities from database: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) ); + + for (size_t i = 0; i < entities_out_size; ++i) { + entities_out[i] = blueprint->project->get_entity(ids[i]); + } } size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { - return ikarus::util::fetch_count( - blueprint, - ikarus::util::CountQueryData{ - .table_name = "entity_blueprint_links", - .select_field_name = "entity", - .where_field_name = "blueprint", - .relation_desc = "linked entities" - } - ) + return blueprint->project->db->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) .unwrap_value_or(0); } diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp index 80d047e..af2395f 100644 --- a/src/objects/entity.cpp +++ b/src/objects/entity.cpp @@ -4,7 +4,6 @@ #include #include -#include #include IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name) { diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index d28275d..498e321 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -23,12 +22,10 @@ IkarusProperty::Data const & IkarusProperty::get_data() const { } cppbase::Result IkarusProperty::get_property_type(IkarusProject * project, IkarusId id) { - auto * ctx = project->get_function_context(); - VTRY( auto const type, - project->get_db()->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id).on_error([ctx](auto const & err) { - ctx->set_error( + project->db->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id).on_error([project](auto const & err) { + project->set_error( fmt::format("failed to fetch unboxed property type: {}", err), true, IkarusErrorInfo_Source_SubSystem, @@ -41,16 +38,17 @@ cppbase::Result IkarusProperty: } IKA_API void ikarus_property_delete(IkarusProperty * property) { - auto * ctx = property->project->get_function_context(); - - TRYRV(, property->project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([ctx](auto const & err) { - ctx->set_error( - fmt::format("failed to delete property from objects table: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - })); + TRYRV( + , + property->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([property](auto const & err) { + property->project->set_error( + fmt::format("failed to delete property from objects table: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); property->project->uncache(property); } @@ -60,15 +58,13 @@ IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { } IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { - auto * ctx = property->project->get_function_context(); - VTRYRV( auto const source, nullptr, - property->project->get_db() + property->project->db ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) - .on_error([ctx](auto const & err) { - ctx->set_error( + .on_error([property](auto const & err) { + property->project->set_error( fmt::format("failed to fetch property's source: {}", err), true, IkarusErrorInfo_Source_SubSystem, @@ -81,7 +77,7 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->project->get_blueprint(source)}; case IkarusObjectType_Entity: return new IkarusPropertySource{property->project->get_entity(source)}; default: { - ctx->set_error( + property->project->set_error( fmt::format("PropertySource is neither blueprint nor entity"), true, IkarusErrorInfo_Source_LibIkarus, @@ -94,15 +90,13 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p } IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) { - auto * ctx = property->project->get_function_context(); - VTRYRV( auto const value, nullptr, - property->project->get_db() + property->project->db ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id) - .on_error([ctx](auto const & err) { - ctx->set_error( + .on_error([property](auto const & err) { + property->project->set_error( fmt::format("failed to fetch property's default value: {}", err), true, IkarusErrorInfo_Source_SubSystem, diff --git a/src/objects/util.hpp b/src/objects/util.hpp deleted file mode 100644 index 2779478..0000000 --- a/src/objects/util.hpp +++ /dev/null @@ -1,241 +0,0 @@ -#pragma once - -#include "util.hpp" - -#include - -#include -#include - -#include -#include - -#include - -namespace ikarus::util { - -struct EmptyNameError {}; - -COMPOUND_ERROR(InsertObjectError, EmptyNameError, sqlitecpp::TransactionError); - -template -[[nodiscard]] cppbase::Result, InsertObjectError> insert_object( - IkarusProject * project, - IkarusObjectType type, - std::string_view name, - InsertFunction insert_function, - ObjectFactory object_factory -) { - auto const * object_type_str = ikarus_object_type_to_string(type); - - auto * ctx = project->get_function_context(); - - if (cppbase::is_empty_or_blank(name)) { - ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return cppbase::err(EmptyNameError{}); - } - - VTRY( - auto const id, - project->get_db() - ->transact([&](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(type), name)); - - auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - - TRY(insert_function(db, id)); - - return cppbase::ok(id); - }) - .on_error([&](auto const & err) { - ctx->set_error( - fmt::format("unable to insert {} into database: {}", object_type_str, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - return cppbase::ok(object_factory(id)); -} - -template - requires std::derived_from -void delete_object(Object * object) { - auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - - auto * ctx = object->project->get_function_context(); - - TRYRV(, object->project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const & err) { - ctx->set_error( - fmt::format("failed to delete {} from objects table: {}", object_type_str, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - })); - - object->project->uncache(object); -} - -struct SingleQueryData { - std::string_view table_name; - std::string_view select_field_name; -}; - -template - requires std::derived_from -cppbase::Result fetch_single_field(Object const * object, SingleQueryData const & query_data) { - auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - - auto * ctx = object->project->get_function_context(); - - VTRY( - T value, - object->project->get_db() - ->template query_one( - fmt::format("SELECT `{}` FROM `{}` WHERE `id` = ?", query_data.select_field_name, query_data.table_name), - object->id - ) - .on_error([&](auto const & err) { - ctx->set_error( - fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.select_field_name, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - return value; -} - -struct MultipleBufferQueryData { - std::string_view table_name; - std::string_view select_field_name; - std::string_view where_field_name; - std::string_view relation_desc; -}; - -template - requires std::derived_from -void fetch_multiple_buffered( - Object const * object, - MultipleBufferQueryData const & query_data, - Mapped * mapped_buffer, - size_t buffer_size, - F transformer -) - requires cppbase::is_result_with_value_type_v> -{ - auto * ctx = object->project->get_function_context(); - - auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - - Selected select_buffer[buffer_size]; - - TRYRV( - , - object->project->get_db() - ->template query_many_buffered( - fmt::format( - "SELECT `{}` FROM `{}` WHERE `{}` = ?", - query_data.select_field_name, - query_data.table_name, - query_data.where_field_name - ), - select_buffer, - buffer_size, - object->id - ) - .on_error([&](auto const & err) { - ctx->set_error( - fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.relation_desc, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - for (size_t i = 0; i < buffer_size; ++i) { - VTRYRV(mapped_buffer[i], , transformer(object->project, ctx, select_buffer[i])); - } -} - -struct CountQueryData { - std::string_view table_name; - std::string_view select_field_name; - std::string_view where_field_name; - std::string_view relation_desc; -}; - -template - requires std::derived_from -cppbase::Result fetch_count(Object const * object, CountQueryData const & query_data) { - auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - - auto * ctx = object->project->get_function_context(); - - VTRY( - auto count, - object->project->get_db() - ->template query_one( - fmt::format( - "SELECT COUNT(`{}`) FROM `{}` WHERE `{}` = ?;", - query_data.select_field_name, - query_data.table_name, - query_data.where_field_name - ), - object->id - ) - .on_error([&](auto const & err) { - ctx->set_error( - fmt::format("failed to fetch {} {} count: {}", object_type_str, query_data.relation_desc, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - return cppbase::ok(static_cast(count)); -} - -template -struct ExistsQueryData { - std::string_view table_name; - std::string_view where_field_name; - T where_field_value; - std::string_view relation_desc; -}; - -template - requires std::derived_from -cppbase::Result check_exists(Object const * object, ExistsQueryData const & query_data) { - auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - - auto * ctx = object->project->get_function_context(); - - VTRY( - auto exists, - object->project->get_db() - ->template query_one( - fmt::format("SELECT EXISTS(SELECT 1 FROM `{}` WHERE `{}` = ?);", query_data.table_name, query_data.where_field_name), - query_data.where_field_value - ) - .on_error([&](auto const & err) { - ctx->set_error( - fmt::format("failed to check whether {} {} exists: {}", object_type_str, query_data.relation_desc, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - return cppbase::ok(static_cast(exists)); -} - -} // namespace ikarus::util diff --git a/src/persistence/function_context.cpp b/src/persistence/function_context.cpp deleted file mode 100644 index f056308..0000000 --- a/src/persistence/function_context.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "function_context.hpp" - -IkarusFunctionContext::IkarusFunctionContext(IkarusProject * project): - _project{project} {} - -IkarusFunctionContext::~IkarusFunctionContext() { - if (_project->_function_contexts.size() == 1) { - if (_project->error_message_buffer.empty()) { - _project->error_message_buffer.push_back('\0'); - } else { - _project->error_message_buffer[0] = '\0'; - } - - _project->error_infos = {}; - } - - _project->_function_contexts.pop_back(); -} diff --git a/src/persistence/function_context.hpp b/src/persistence/function_context.hpp deleted file mode 100644 index 663ff2d..0000000 --- a/src/persistence/function_context.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include - -#include - -#include - -struct IkarusFunctionContext { -public: - explicit IkarusFunctionContext(struct IkarusProject * project); - - IkarusFunctionContext(IkarusFunctionContext const &) noexcept = default; - IkarusFunctionContext(IkarusFunctionContext &&) noexcept = default; - - auto operator=(IkarusFunctionContext const &) noexcept -> IkarusFunctionContext & = default; - auto operator=(IkarusFunctionContext &&) noexcept -> IkarusFunctionContext & = default; - - ~IkarusFunctionContext(); - -public: - template - requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) - auto set_error(std::string_view error_message, bool log_error, Infos... infos) -> void { - if (error_message.size() > _project->error_message_buffer.size()) { - _project->error_message_buffer.resize(error_message.size() + 1); - } - - for (int i = 0; i < error_message.size(); ++i) { - _project->error_message_buffer[i] = error_message[i]; - } - - _project->error_message_buffer[error_message.size()] = '\0'; - _project->error_infos = {infos...}; - - if (log_error) { - LOG_ERROR("Error({}): {}", fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), error_message); - } - } - -private: - struct IkarusProject * _project; -}; diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index 0be9991..da56280 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -8,27 +8,14 @@ #include #include #include -#include -auto IkarusProject::get_name() const -> std::string_view { - return _name; -} - -auto IkarusProject::get_path() const -> std::filesystem::path const & { - return _path; -} - -auto IkarusProject::get_db() -> sqlitecpp::Connection * { - return _db.get(); -} - -auto IkarusProject::get_db() const -> sqlitecpp::Connection const * { - return _db.get(); -} - -auto IkarusProject::get_function_context() -> IkarusFunctionContext * { - return &_function_contexts.emplace_back(this); -} +IkarusProject::IkarusProject(std::string_view name, std::filesystem::path path): + name{name}, + path{path}, + db{nullptr}, + _blueprints{}, + _properties{}, + _entities{} {} IkarusBlueprint * IkarusProject::get_blueprint(IkarusId id) { return get_cached_object(id, this->_blueprints); diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index 2359f2e..f113f24 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -11,20 +11,10 @@ #include #include -constexpr inline auto MAXIMUM_ERROR_INFOS = 8; - /// \private struct IkarusProject { public: - [[nodiscard]] auto get_name() const -> std::string_view; - - [[nodiscard]] auto get_path() const -> std::filesystem::path const &; - - [[nodiscard]] auto get_db() -> sqlitecpp::Connection *; - [[nodiscard]] auto get_db() const -> sqlitecpp::Connection const *; - -public: - [[nodiscard]] auto get_function_context() -> struct IkarusFunctionContext *; + IkarusProject(std::string_view name, std::filesystem::path path); public: [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; @@ -53,19 +43,13 @@ private: cache.erase(object->id); } +public: + std::string name; + std::filesystem::path path; + std::unique_ptr db; + private: - friend struct IkarusFunctionContext; - - std::string _name; - std::filesystem::path _path; - std::unique_ptr _db; - - std::array error_infos; - std::string error_message_buffer; - - std::unordered_map> _blueprints; - std::unordered_map> _properties; - std::unordered_map> _entities; - - std::vector _function_contexts; + std::unordered_map> _blueprints; + std::unordered_map> _properties; + std::unordered_map> _entities; }; From 1ce811d566b581427b52bd18e48aaceabd729788 Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 3 Jan 2024 17:14:26 +0100 Subject: [PATCH 035/166] implement remaining logic Signed-off-by: Folling --- .clang-tidy | 6 +- CMakeLists.txt | 2 +- include/ikarus/errors.h | 87 +++-- include/ikarus/objects/entity.h | 51 ++- include/ikarus/objects/object.h | 16 +- include/ikarus/objects/object_type.h | 3 +- .../objects/properties/number_property.h | 4 +- include/ikarus/objects/properties/property.h | 2 +- .../ikarus/objects/properties/property_type.h | 6 +- .../ikarus/objects/properties/text_property.h | 4 +- .../objects/properties/toggle_property.h | 4 +- include/ikarus/persistence/project.h | 68 +--- include/ikarus/values/entity_property_value.h | 46 +++ include/ikarus/values/value.h | 6 +- src/CMakeLists.txt | 2 + src/errors.cpp | 64 ++-- src/errors.hpp | 93 ++++++ src/objects/blueprint.cpp | 174 +++++----- src/objects/entity.cpp | 297 ++++++++++++++++-- src/objects/object.cpp | 104 ++++++ src/objects/object.hpp | 4 - src/objects/properties/number_property.cpp | 29 ++ src/objects/properties/number_property.hpp | 11 +- src/objects/properties/property.cpp | 131 ++++---- src/objects/properties/property.hpp | 17 +- src/objects/properties/property_source.cpp | 12 + src/objects/properties/property_source.hpp | 5 +- src/objects/properties/text_property.cpp | 29 ++ src/objects/properties/text_property.hpp | 11 +- src/objects/properties/toggle_property.cpp | 30 +- src/objects/properties/toggle_property.hpp | 7 + src/objects/properties/util.hpp | 100 ++++++ src/persistence/migrations.hpp | 40 +++ src/persistence/migrations/m0_genesis.sql | 7 + src/persistence/project.cpp | 233 +++++++++++++- src/persistence/project.hpp | 18 +- src/values/entity_property_value.cpp | 21 ++ src/values/entity_property_value.hpp | 7 + src/values/value.cpp | 2 +- src/values/value.hpp | 46 ++- vendor/sqlitecpp | 2 +- 41 files changed, 1393 insertions(+), 408 deletions(-) create mode 100644 include/ikarus/values/entity_property_value.h create mode 100644 src/errors.hpp create mode 100644 src/objects/properties/util.hpp create mode 100644 src/persistence/migrations.hpp create mode 100644 src/persistence/migrations/m0_genesis.sql create mode 100644 src/values/entity_property_value.cpp create mode 100644 src/values/entity_property_value.hpp diff --git a/.clang-tidy b/.clang-tidy index c2cf005..63731e9 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,7 +1,7 @@ Checks: >- -*, bugprone-*, -bugprone-lambda-function-name, - cppcoreguidelines-*, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes, + cppcoreguidelines-*, -cppcoreguidelines-macro-usage, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes, clang-analyzer-*, google-*, -google-readability-todo, modernize-*, -modernize-use-trailing-return-type, @@ -9,9 +9,9 @@ Checks: >- portability-*, readability-*, -readability-redundant-access-specifiers CheckOptions: - readability-identifier-length.IgnoredParameterNames: '^(db|rc|id)$' + readability-identifier-length.IgnoredParameterNames: '^(db|rc|id|ec)$' readability-identifier-length.IgnoredLoopCounterNames: '^[ij]$' - readability-identifier-length.IgnoredVariableNames: '^(db|rc|id)$' + readability-identifier-length.IgnoredVariableNames: '^(db|rc|id|ec)$' cppcoreguidelines-avoid-do-while.IgnoreMacros: Yes HeaderFileExtensions: - h diff --git a/CMakeLists.txt b/CMakeLists.txt index ae81667..8389d4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ add_subdirectory(vendor) add_subdirectory(include) add_subdirectory(src) -find_package(Boost REQUIRED) +find_package(Boost COMPONENTS system filesystem REQUIRED) add_library( libikarus SHARED diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index 92b16cf..a4887a3 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -4,6 +4,7 @@ /// \author Folling #include +#include /// \addtogroup errors Errors /// \brief Error handling within libikarus @@ -24,94 +25,81 @@ IKARUS_BEGIN_HEADER enum IkarusErrorInfo { /// \brief No error occurred. IkarusErrorInfo_None = 0x0, - /// \brief The error was caused by the client. - IkarusErrorInfo_Client = 0x10100000, - /// \brief The error was caused by a dependency (e.g. boost) of libikarus. - IkarusErrorInfo_Dependency = 0x10200000, - /// \brief The error was caused by the filesystem. - IkarusErrorInfo_Filesystem = 0x10300000, - /// \brief The error was caused by the database. - IkarusErrorInfo_Database = 0x10400000, - /// \brief The error was caused by the underlying OS. - IkarusErrorInfo_OS = 0x10500000, - /// \brief The error was caused by libikarus itself. - IkarusErrorInfo_LibIkarus = 0x10600000, - /// \brief The client misused the API. /// Example: Accessing a resource that does not exist. - IkarusErrorInfo_Client_Misuse = 0x10100001, + IkarusErrorInfo_Client_Misuse = 0x01000001, /// \brief The client provided a null value for a parameter that must not be null. /// Example: Passing null for `ikarus_project_get_name` - IkarusErrorInfo_Client_InvalidNull = 0x10100002, + IkarusErrorInfo_Client_InvalidNull = 0x01000002, /// \brief The client provided an index that was out of bounds for some array. /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3. - IkarusErrorInfo_Client_IndexOutOfBounds = 0x10100003, + IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000003, /// \brief The client provided a numeric value that was out of bounds /// Example: Passing the value 2^32 to an i32 (might be passed as a string). - IkarusErrorInfo_Client_ValueOutOfBounds = 0x10100004, + IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000004, /// \brief The client provided invalid input that doesn't fit in any of the other categories. /// Example: Passing an empty/blank string for a string that must be non-empty/-blank. - IkarusErrorInfo_Client_InvalidInput = 0x10100005, + IkarusErrorInfo_Client_InvalidInput = 0x01000005, /// \brief The client provided valid data in an invalid format. /// Example: Passing a malformed JSON string. - IkarusErrorInfo_Client_InvalidFormat = 0x10100006, + IkarusErrorInfo_Client_InvalidFormat = 0x01000006, /// \brief The client violated a constraint. /// \details This error is most likely caused by endusers. /// Example: A user tries to set the age of a character to an value outside of their specified range. - IkarusErrorInfo_Client_ConstraintViolated = 0x10100007, + IkarusErrorInfo_Client_ConstraintViolated = 0x10000007, + + // 0x02 reserved for dependency errors - /// \brief A file was not found. - IkarusErrorInfo_Filesystem_NotFound = 0x10300001, /// \brief A file or directory already exists. - IkarusErrorInfo_Filesystem_AlreadyExists = 0x10300002, + IkarusErrorInfo_Filesystem_AccessIssue = 0x03000001, + /// \brief A file was not found. + IkarusErrorInfo_Filesystem_NotFound = 0x03000002, + /// \brief A file or directory already exists. + IkarusErrorInfo_Filesystem_AlreadyExists = 0x03000003, /// \brief Missing permissions to access a file or directory. - IkarusErrorInfo_Filesystem_MissingPermissions = 0x10300003, + IkarusErrorInfo_Filesystem_MissingPermissions = 0x03000004, /// \brief Insufficient space to perform an operation. - IkarusErrorInfo_Filesystem_InsufficientSpace = 0x10300004, + IkarusErrorInfo_Filesystem_InsufficientSpace = 0x03000005, /// \brief A path is invalid. - IkarusErrorInfo_Filesystem_InvalidPath = 0x10300005, + IkarusErrorInfo_Filesystem_InvalidPath = 0x03000006, /// \brief A database connection failed. - IkarusErrorInfo_Database_ConnectionFailed = 0x10400001, + IkarusErrorInfo_Database_ConnectionFailed = 0x04000001, /// \brief A database query failed. - IkarusErrorInfo_Database_QueryFailed = 0x10400002, + IkarusErrorInfo_Database_QueryFailed = 0x04000002, /// \brief A database migration failed. - IkarusErrorInfo_Database_MigrationFailed = 0x10400003, + IkarusErrorInfo_Database_MigrationFailed = 0x04000003, /// \brief A database is in an invalid state. This indicates a corrupt project. /// Example: An entity is linked to a non-existant blueprint. - IkarusErrorInfo_Database_InvalidState = 0x10400004, + IkarusErrorInfo_Database_InvalidState = 0x04000004, /// \brief A system call failed. - IkarusErrorInfo_OS_SystemCallFailed = 0x10500001, + IkarusErrorInfo_OS_SystemCallFailed = 0x05000001, /// \brief A system call returned an invalid value. - IkarusErrorInfo_OS_InvalidReturnValue = 0x10500002, + IkarusErrorInfo_OS_InvalidReturnValue = 0x05000002, /// \brief An OOM error occurred. - IkarusErrorInfo_OS_InsufficientMemory = 0x10500003, + IkarusErrorInfo_OS_InsufficientMemory = 0x05000003, /// \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, + /// whereas the former may imply that the state is ephemeral, e.g. data within a function. /// Example: The name of an object is found to be invalid UTF8. - IkarusErrorInfo_LibIkarus_InvalidState = 0x20030001, + IkarusErrorInfo_LibIkarus_InvalidState = 0x06000001, + /// \brief libikarus is unable to perform a certain operation that should succeed. + IkarusErrorInfo_LibIkarus_CannotPerformOperation = 0x06000002, /// \brief libikarus is unable to perform a certain operation within a given timeframe. /// Example: A query takes longer than the timeout. - IkarusErrorInfo_LibIkarus_Timeout = 0x20030003, + IkarusErrorInfo_LibIkarus_Timeout = 0x06000003, }; -/// \brief The data limits for an error. -enum IkarusErrorDataLimit { - /// \brief The maximum number of error infos that can be stored in an error. - IkarusErrorDataLimit_MaxErrorInfos = 8, - /// \brief The maximum length of an error message. - IkarusErrorDataLimit_MaxMessageLength = 128, -}; +size_t const IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT = 128; /// \brief The data stored for an error struct IkarusErrorData { - /// \brief The scope of the error. - /// \details This array may at most hold #IkarusErrorDataLimit_MaxErrorInfos elements. - /// The first occurrence of #IkarusErrorInfo_None signifies the end of the array. If this happens at idx x== 0, no error occurred. - IkarusErrorInfo infos[IkarusErrorDataLimit_MaxErrorInfos]; + /// \brief The error type + IkarusErrorInfo info; - char message[IkarusErrorDataLimit_MaxMessageLength]; + char message[IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT]; }; /// \brief Gets the name of an error info. @@ -128,11 +116,6 @@ IKA_API bool ikarus_error_data_is_success(IkarusErrorData const * data); /// \param data The error data to check. /// \return True if the error data is an error, false otherwise. IKA_API bool ikarus_error_data_is_error(IkarusErrorData const * data); -/// \brief Formats the error data in a reasonable but unspecified way. -/// \param data The error data to format. -/// \return The formatted error data. -/// \remark Ownership of the returned pointer is passed to the user and must be freed at their leisure using ikarus_free. -IKA_API char const * ikarus_error_data_pretty_format(IkarusErrorData const * data); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index cfc09f3..9610230 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -89,6 +89,30 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru /// \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 the blueprints an entity is linked to. +/// \param entity The entity to get the blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprints_out The buffer to write the blueprints to. +/// \pre \li Must not be null. +/// \param blueprints_out_size The size of the buffer. +/// \param error_out \see errors.h +/// \see ikarus_entity_get_linked_blueprint_count +IKA_API void ikarus_entity_get_linked_blueprints( + IkarusEntity const * entity, + struct IkarusBlueprint ** blueprints_out, + size_t blueprints_out_size, + IkarusErrorData * error_out +); + +/// \brief Gets the number of blueprints an entity is linked to. +/// \param entity The entity to get the number of blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The number of blueprints or undefined if an error occurs. +IKA_API size_t ikarus_entity_get_linked_blueprint_count(IkarusEntity const * entity, IkarusErrorData * error_out); + /// \brief Checks if an entity has a specific property. /// \param entity The entity to check. /// \pre \li Must not be null. @@ -97,17 +121,9 @@ IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct I /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return True if the entity has the property, false otherwise. +/// \return False if an error occurs or the entity does not have the property, true otherwise. IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); -/// \brief Gets the number of properties of an entity. -/// \param entity The entity to get the number of properties of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity, IkarusErrorData * error_out); - /// \brief Gets the properties of an entity. /// \param entity The entity to get the properties of. /// \pre \li Must not be null. @@ -116,6 +132,7 @@ IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity, Ika /// \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, @@ -123,9 +140,16 @@ IKA_API void ikarus_entity_get_properties( IkarusErrorData * error_out ); +/// \brief Gets the number of properties of an entity. +/// \param entity The entity to get the number of properties of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The number of properties or undefined if an error occurs. +IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity, IkarusErrorData * error_out); + /// \brief Gets the value of a property of an entity. -/// \details If the entity has never set the value of the property, the default value is returned (which may be -/// undefined). +/// \details If the entity has never set the value of the property, the default value is returned (which may be undefined). /// \param entity The entity to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -134,9 +158,8 @@ IKA_API void ikarus_entity_get_properties( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The value of the property or null if the entity does not have the property or an error occurs. -/// \remark Must be freed using -/// #ikarus_free. -IKA_API struct IkarusEntityValue * +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusEntityPropertyValue * ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Sets the value of a property of an entity. diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index 2aa3d0c..9dd3b39 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -19,6 +19,13 @@ IKARUS_BEGIN_HEADER /// \brief A generic object. Wraps all types of objects, including folders. struct IkarusObject; +/// \brief Fetches the project of an object. +/// \param object The object to fetch the project from. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return The project of the object or null if an error occurs. +IKA_API struct IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErrorData * error_out); + /// \brief Compares two objects for equality. /// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the /// same object. @@ -35,9 +42,6 @@ IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const /// \param blueprint_visitor The function to call if the object is a blueprint. Skipped if null. /// \param property_visitor The function to call if the object is a property. Skipped if null. /// \param entity_visitor The function to call if the object is an entity. Skipped if null. -/// \param blueprint_folder_visitor The function to call if the object is a blueprint folder. Skipped if null. -/// \param property_folder_visitor The function to call if the object is a property folder. Skipped if null. -/// \param entity_folder_visitor The function to call if the object is an entity folder. Skipped if null. /// \param data The data passed to the visitor functions. /// \param error_out \see errors.h IKA_API void ikarus_object_visit( @@ -45,9 +49,6 @@ IKA_API void ikarus_object_visit( void (*blueprint_visitor)(struct IkarusBlueprint *, void *), void (*property_visitor)(struct IkarusProperty *, void *), void (*entity_visitor)(struct IkarusEntity *, void *), - void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder *, void *), - void (*property_folder_visitor)(struct IkarusPropertyFolder *, void *), - void (*entity_folder_visitor)(struct IkarusEntityFolder *, void *), void * data, IkarusErrorData * error_out ); @@ -58,9 +59,6 @@ IKA_API void ikarus_object_visit_const( void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), void (*property_visitor)(struct IkarusProperty const *, void *), void (*entity_visitor)(struct IkarusEntity const *, void *), - void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder const *, void *), - void (*property_folder_visitor)(struct IkarusPropertyFolder const *, void *), - void (*entity_folder_visitor)(struct IkarusEntityFolder const *, void *), void * data, IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 7dd3507..e30bb3c 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -25,10 +25,9 @@ enum IkarusObjectType { /// \brief Converts an IkarusObjectType to a string. /// \param type The type to convert. -/// \param error_out \see errors.h /// \return The string representation of the type. /// \remark The returned string must not be freed. -char const * ikarus_object_type_to_string(IkarusObjectType type, IkarusErrorData * error_out); +char const * ikarus_object_type_to_string(IkarusObjectType type); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 5d24f87..8ca4ffd 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -46,7 +46,7 @@ ikarus_number_property_get_default_value(struct IkarusNumberProperty * property, /// \param property The number property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param default_value The default value. +/// \param new_default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. /// \param error_out \see errors.h @@ -54,7 +54,7 @@ ikarus_number_property_get_default_value(struct IkarusNumberProperty * property, /// default values and other settings. IKA_API void ikarus_number_property_set_default_value( struct IkarusNumberProperty * property, - struct IkarusNumberValue * default_value, + struct IkarusNumberValue * new_default_value, IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index 34f2d9a..7a94079 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -89,7 +89,7 @@ IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusPro /// \param error_out \see errors.h /// \return The default value of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out); +IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Visits a property. Calling the appropriate function for the property's type. /// \param property The property to visit. diff --git a/include/ikarus/objects/properties/property_type.h b/include/ikarus/objects/properties/property_type.h index ee6ec19..893cdca 100644 --- a/include/ikarus/objects/properties/property_type.h +++ b/include/ikarus/objects/properties/property_type.h @@ -15,11 +15,11 @@ IKARUS_BEGIN_HEADER /// available. enum IkarusPropertyType { /// \brief A true/false boolean-esque value. - IkarusPropertyType_Toggle, + IkarusPropertyType_Toggle = 0x10000000, /// \brief A numeric value, limited to IEEE 80 bit floating point numbers. - IkarusPropertyType_Number, + IkarusPropertyType_Number = 0x20000000, /// \brief An arbitrary UTF-8 textual value. - IkarusPropertyType_Text, + IkarusPropertyType_Text = 0x30000000, }; IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index 0d240b2..0408614 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -45,7 +45,7 @@ IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct I /// \param property The text property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param default_value The default value. +/// \param new_default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. /// \param error_out \see errors.h @@ -53,7 +53,7 @@ IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct I /// default values and other settings. IKA_API void ikarus_text_property_set_default_value( struct IkarusTextProperty * property, - struct IkarusTextValue * default_value, + struct IkarusTextValue * new_default_value, IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index e084123..892d418 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -46,7 +46,7 @@ ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, /// \param property The toggle property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param default_value The default value. +/// \param new_default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. /// \param error_out \see errors.h @@ -54,7 +54,7 @@ ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, /// default values and other settings. IKA_API void ikarus_toggle_property_set_default_value( struct IkarusToggleProperty * property, - struct IkarusToggleValue * default_value, + struct IkarusToggleValue * new_default_value, IkarusErrorData * error_out ); diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 93c4e11..c4406f8 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -51,39 +51,13 @@ IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name, Ikaru /// ikarus_project_delete IKA_API IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_out); -/// \brief Copies a project to a new location. -/// \details The new project is not opened. -/// \param project The project to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param target_path The new location of the project. -/// \pre \li Must not be null. -/// \pre \li Must point to a valid unused path on the system. -/// \param target_name The name of the new project. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param error_out \see errors.h -/// \remark If successful the project connection remains intact. The previous location will still exist. -IKA_API void -ikarus_project_copy(IkarusProject const * project, char const * target_path, char const * target_name, IkarusErrorData * error_out); - -/// \brief Deletes a project and all its associated data from the filesystem. -/// \param project The project to delete. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \remark also frees the project. -/// \remark In-Memory projects will just be freed. -/// \remark If deletion fails, the project pointer remains intact. -IKA_API void ikarus_project_delete(IkarusProject * project, IkarusErrorData * error_out); - /// \brief Gets the name of a project. /// \param project The project to get the name of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The name of the project. -/// \remark Must be freed using #ikarus_free. +/// \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. @@ -102,30 +76,9 @@ IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_n /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The path of the project. -/// \remark Must be freed using #ikarus_free. +/// \remark Ownership remains with libikarus, must not be freed. IKA_API char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out); -/// \brief Moves a project to a new location. -/// \param project The project to move. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param target_path The new location of the project. -/// \pre \li Must not be null. -/// \pre \li Must point to a valid unused path on the system. -/// \param error_out \see errors.h -/// \remark If successful the project connection remains intact. The previous location will not exist anymore. -/// \remark Due to the nature of filesystems this function may not be atomic. -IKA_API void ikarus_project_move(IkarusProject * project, char const * target_path, IkarusErrorData * error_out); - -/// \brief Gets the blueprint root folder of a project. -/// \param project The project to get the blueprint root folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The blueprint root folder of the project or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder(IkarusProject const * project, IkarusErrorData * error_out); - /// \brief Gets the blueprints of a project. /// \param project The project to get the blueprints of. /// \pre \li Must not be null. @@ -135,7 +88,7 @@ IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder( /// \param blueprints_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_blueprints( - IkarusProject const * project, + IkarusProject * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size, IkarusErrorData * error_out @@ -147,16 +100,7 @@ IKA_API void ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); - -/// \brief Gets the entity root folder of a project. -/// \param project The project to get the entity root folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The entity root folder of the project or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(IkarusProject const * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject * project, IkarusErrorData * error_out); /// \brief Gets the entities of a project. /// \param project The project to get the entities of. @@ -167,7 +111,7 @@ IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(Ikarus /// \param entities_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_entities( - IkarusProject const * project, + IkarusProject * project, struct IkarusEntity ** entities_out, size_t entities_out_size, IkarusErrorData * error_out @@ -179,7 +123,7 @@ IKA_API void ikarus_project_get_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of entities or undefined if an error occurs. -IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/entity_property_value.h b/include/ikarus/values/entity_property_value.h new file mode 100644 index 0000000..c408f00 --- /dev/null +++ b/include/ikarus/values/entity_property_value.h @@ -0,0 +1,46 @@ +#pragma once + +/// \file entity_property_value.h +/// \author Folling + +/// \defgroup entity_property_values EntityPropertyValue +/// \brief Values in relation to an entity and one of its properties. +/// @{ + +#include +#include +#include + +IKARUS_BEGIN_HEADER + +/// \brief Like an \ref value.h "IkarusValue", but in relation to an entity and one of its properties +struct IkarusEntityPropertyValue; + +/// \brief Fetches the entity of an entity property value. +/// \param value The entity property value. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return The entity of the entity property value. +/// \remark This value is owned by the entity property value and must not be freed directly. +struct IkarusEntity const * ikarus_entity_property_value_get_entity(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); + +/// \brief Fetches the property of an entity property value. +/// \param value The entity property value. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return The property of the entity property value. +/// \remark This value is owned by the entity property value and must not be freed directly. +struct IkarusProperty const * +ikarus_entity_property_value_get_property(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); + +/// \brief Fetches the value of an entity property value. +/// \param value The entity property value. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return The value of the entity property value. +/// \remark This value is owned by the entity property value and must not be freed directly. +struct IkarusValue const * ikarus_entity_property_value_get_value(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index 3fcaf63..fbc50e1 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -3,9 +3,6 @@ /// \file value.h /// \author Folling -#include -#include - /// \defgroup values Values /// \brief The values of properties. /// \details Each entity has a value for each property it is associated with. @@ -17,6 +14,9 @@ /// property's settings. \see PropertyType /// @{ +#include +#include + IKARUS_BEGIN_HEADER /// \brief The common type for all values. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b00545..0963d2e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,8 @@ file( FILES "*.hpp" "*.cpp" + "*.h" + "*.c" ) set(SOURCE_FILES ${FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/errors.cpp b/src/errors.cpp index 32b2c99..4441fe9 100644 --- a/src/errors.cpp +++ b/src/errors.cpp @@ -11,61 +11,39 @@ char const * get_error_info_name(IkarusErrorInfo info) { switch (info) { case IkarusErrorInfo_None: return "None"; - case IkarusErrorInfo_Client: return "Client"; - case IkarusErrorInfo_Dependency: return "Dependency"; - case IkarusErrorInfo_Filesystem: return "Filesystem"; - case IkarusErrorInfo_Database: return "Database"; - case IkarusErrorInfo_OS: return "OS"; - case IkarusErrorInfo_LibIkarus: return "libikarus"; - case IkarusErrorInfo_Client_Misuse: return "Misuse"; - case IkarusErrorInfo_Client_InvalidInput: return "InvalidInput"; - case IkarusErrorInfo_Client_InvalidFormat: return "InvalidFormat"; - case IkarusErrorInfo_Client_ConstraintViolated: return "ConstraintViolated"; + case IkarusErrorInfo_Client_Misuse: return "Client::Misuse"; + case IkarusErrorInfo_Client_InvalidInput: return "Client::InvalidInput"; + case IkarusErrorInfo_Client_InvalidFormat: return "Client::InvalidFormat"; + case IkarusErrorInfo_Client_ConstraintViolated: return "Client::ConstraintViolated"; - case IkarusErrorInfo_Filesystem_NotFound: return "NotFound"; - case IkarusErrorInfo_Filesystem_AlreadyExists: return "AlreadyExists"; - case IkarusErrorInfo_Filesystem_MissingPermissions: return "MissingPermissions"; - case IkarusErrorInfo_Filesystem_InsufficientSpace: return "InsufficientSpace"; - case IkarusErrorInfo_Filesystem_InvalidPath: return "InvalidPath"; + case IkarusErrorInfo_Filesystem_NotFound: return "Filesystem::NotFound"; + case IkarusErrorInfo_Filesystem_AlreadyExists: return "Filesystem::AlreadyExists"; + case IkarusErrorInfo_Filesystem_MissingPermissions: return "Filesystem::MissingPermissions"; + case IkarusErrorInfo_Filesystem_InsufficientSpace: return "Filesystem::InsufficientSpace"; + case IkarusErrorInfo_Filesystem_InvalidPath: return "Filesystem::InvalidPath"; - case IkarusErrorInfo_Database_ConnectionFailed: return "ConnectionFailed"; - case IkarusErrorInfo_Database_QueryFailed: return "QueryFailed"; - case IkarusErrorInfo_Database_MigrationFailed: return "MigrationFailed"; - case IkarusErrorInfo_Database_InvalidState: return "InvalidState"; + case IkarusErrorInfo_Database_ConnectionFailed: return "Database::ConnectionFailed"; + case IkarusErrorInfo_Database_QueryFailed: return "Database::QueryFailed"; + case IkarusErrorInfo_Database_MigrationFailed: return "Database::MigrationFailed"; + case IkarusErrorInfo_Database_InvalidState: return "Database::InvalidState"; - case IkarusErrorInfo_OS_SystemCallFailed: return "SystemCallFailed"; - case IkarusErrorInfo_OS_InvalidReturnValue: return "InvalidReturnValue"; - case IkarusErrorInfo_OS_InsufficientMemory: return "InsufficientMemory"; + case IkarusErrorInfo_OS_SystemCallFailed: return "OS::SystemCallFailed"; + case IkarusErrorInfo_OS_InvalidReturnValue: return "OS::InvalidReturnValue"; + case IkarusErrorInfo_OS_InsufficientMemory: return "OS::InsufficientMemory"; - case IkarusErrorInfo_LibIkarus_InvalidState: return "InvalidState"; - case IkarusErrorInfo_LibIkarus_Timeout: return "Timeout"; + case IkarusErrorInfo_LibIkarus_InvalidState: return "LibIkarus::InvalidState"; + case IkarusErrorInfo_LibIkarus_CannotPerformOperation: return "LibIkarus::CannotPerformOperation"; + case IkarusErrorInfo_LibIkarus_Timeout: return "LibIkarus::Timeout"; default: return "Invalid"; } } bool ikarus_error_data_is_success(IkarusErrorData const * data) { - return data->infos[0] == IkarusErrorInfo_None; + return data->info == IkarusErrorInfo_None; } bool ikarus_error_data_is_error(IkarusErrorData const * data) { - return data->infos[0] != IkarusErrorInfo_None; -} - -char const * ikarus_error_data_pretty_format(IkarusErrorData const * data) { - if (ikarus_error_data_is_success(data)) { - return "Success"; - } - - auto const formatted = fmt::format( - "{} - {}", - fmt::join( - data->infos | std::views::take_while(cppbase::pred_ne(IkarusErrorInfo_None)) | std::views::transform(get_error_info_name), - "->" - ), - data->message - ); - - return strndup(formatted.data(), formatted.size()); + return data->info != IkarusErrorInfo_None; } diff --git a/src/errors.hpp b/src/errors.hpp new file mode 100644 index 0000000..0a9941f --- /dev/null +++ b/src/errors.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include + +#include + +inline void safe_strcpy(char * dest, std::string_view src, size_t dest_size) { + for (int i = 0; i < dest_size; ++i) { + if (src[i] == '\0') { + dest[i] = '\0'; + return; + } + + dest[i] = src[i]; + } +} + +#define IKARUS_SET_ERROR(msg, err_info) \ + if (error_out != nullptr) { \ + safe_strcpy(static_cast(error_out->message), msg, IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT); \ + error_out->info = err_info; \ + } + +#define IKARUS_FAIL(ret, msg, err_info) \ + IKARUS_SET_ERROR(msg, err_info); \ + return ret + +#define IKARUS_FAIL_IF(condition, ret, msg, err_info) \ + if (condition) { \ + IKARUS_SET_ERROR(msg, err_info) \ + return ret; \ + } + +#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_TRY_OR_FAIL_IMPL(var_name, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ + return var_name; \ + } + +#define IKARUS_TRY_OR_FAIL(msg, err_info, ...) IKARUS_TRY_OR_FAIL_IMPL(CPPBASE_UNIQUE_NAME(result), msg, err_info, __VA_ARGS__); + +#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + 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, ...) \ + 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, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ + return var_name; \ + } \ + value = var_name.unwrap_value() + +#define IKARUS_VTRY_OR_FAIL(value, msg, err_info, ...) \ + 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, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ + return ret; \ + } \ + value = var_name.unwrap_value() + +#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__); + +#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("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); diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index 117cbfc..971bd13 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -1,11 +1,13 @@ #include "ikarus/objects/blueprint.h" +#include "ikarus/objects/properties/property.h" #include "objects/blueprint.hpp" #include #include #include +#include #include #include #include @@ -13,117 +15,149 @@ IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusObject{project, id} {} -IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { - if (cppbase::is_empty_or_blank(name)) { - project->set_error("blueprint name must not be empty", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); +IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), nullptr, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); - return nullptr; - } - - project->db - ->transact([name](auto * db) { + IKARUS_VTRYRV_OR_FAIL( + IkarusId const id, + nullptr, + "failed to create blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->transact([name](auto * db) -> cppbase::Result { TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Blueprint, name)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id)); - return cppbase::ok(); + return cppbase::ok(id); }) - .on_error([project](auto const & err) { - project->set_error( - fmt::format("failed to create blueprint: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }); + ); + + return project->get_blueprint(id); } -void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { - blueprint->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id).on_error([blueprint](auto const & err) { - blueprint->project->set_error( - fmt::format("failed to delete blueprint from objects table: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }); +void ikarus_blueprint_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 `objects` WHERE `id` = ?", blueprint->id) + ); + + blueprint->project->uncache(blueprint); } void ikarus_blueprint_get_properties( IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, - size_t properties_out_size + size_t properties_out_size, + IkarusErrorData * error_out ) { - IkarusId ids[properties_out_size]; + IKARUS_FAIL_IF_NULL(blueprint, ); + IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, ); + IKARUS_FAIL_IF_NULL(properties_out, ); - TRYRV( + if (properties_out_size == 0) { + return; + } + + std::tuple ids_and_types[properties_out_size]; + + IKARUS_TRYRV_OR_FAIL( , - blueprint->project->db - ->query_many_buffered("SELECT `id` FROM `properties` WHERE `source` = ?", ids, properties_out_size, blueprint->id) - .on_error([&](auto const & err) { - blueprint->project->set_error( - fmt::format("failed to fetch blueprint properties from database: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); + "unable to fetch blueprint properties from database: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_many_buffered( + "SELECT `id` FROM `properties` WHERE `source` = ?", + ids_and_types, + properties_out_size, + blueprint->id + ) + ) - // not atomic, could be switched to two loops if necessary for (size_t i = 0; i < properties_out_size; ++i) { - IkarusId id = ids[i]; - - VTRYRV(auto const type, , IkarusProperty::get_property_type(blueprint->project, id)); + 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) { - return blueprint->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", blueprint->id) - .unwrap_value_or(0); +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("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", blueprint->id) + ); + + return ret; } void ikarus_blueprint_get_linked_entities( IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, - size_t entities_out_size + 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; + } + IkarusId ids[entities_out_size]; - TRYRV( + IKARUS_TRYRV_OR_FAIL( , - blueprint->project->db - ->query_many_buffered( - "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", - ids, - entities_out_size, - blueprint->id - ) - .on_error([&](auto const & err) { - blueprint->project->set_error( - fmt::format("failed to fetch linked entities from database: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); + "unable to fetch blueprint linked entities from database: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_many_buffered( + "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) { - return blueprint->project->db->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) - .unwrap_value_or(0); +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("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) + ); + + return ret; } -IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint) { +// No existence checks here for performance reasons. All methods on IkarusObject perform this check anyway. + +IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(blueprint, nullptr); + return static_cast(blueprint); } -IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint) { +IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(blueprint, nullptr); + return static_cast(blueprint); } diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp index af2395f..174123e 100644 --- a/src/objects/entity.cpp +++ b/src/objects/entity.cpp @@ -1,36 +1,289 @@ #include "entity.hpp" +#include "values/value.hpp" + #include +#include #include -#include +#include #include +#include -IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name) { - return ikarus::util::insert_object( - project, - IkarusObjectType_Entity, - name, - [](auto * db, IkarusId id) { return db->execute("INSERT INTO `entities`(`id`) VALUES(?)", id); }, - [project](IkarusId id) { return project->get_entity(id); } - ).unwrap_value_or(nullptr); +IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + 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( + IkarusId const id, + nullptr, + "failed to create entity: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->transact([name](auto * db) -> cppbase::Result { + TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Entity, name)); + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Entity); + TRY(db->execute("INSERT INTO `entities`(`id`) VALUES(?)", id)); + return cppbase::ok(id); + }) + ); + + return project->get_entity(id); } -void ikarus_entity_delete(IkarusEntity * entity) { - ikarus::util::delete_object(entity); +void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(entity, ); + IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); + + IKARUS_TRYRV_OR_FAIL( + , + "unable to delete entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute("DELETE FROM `objects` WHERE `id` == ?", entity->id) + ); + + entity->project->uncache(entity); } -bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint) { - return ikarus::util::check_exists( - entity, - ikarus::util::ExistsQueryData{ - .table_name = "entity_blueprint_links", - .where_field_name = "blueprint", - .where_field_value = blueprint->id, - .relation_desc = "linked blueprints" - } +bool ikarus_entity_is_linked_to_blueprint( + IkarusEntity const * entity, + struct IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, false); + IKARUS_FAIL_IF_OBJECT_MISSING(entity, false); + 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( + "SELECT EXISTS(SELECT 1 FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?)", + entity->id, + blueprint->id + ) ) - .unwrap_value_or(false); + + return ret; } -bool ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint) {} +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) + ); +} + +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; + } + + IkarusId 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( + "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("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", entity->id) + ); + + return ret; +} + +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); + + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + false, + "unable to check whether entity has property: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT EXISTS(SELECT 1 FROM `entity_properties` WHERE `entity` = ? AND `property` = ?)", + entity->id, + property->id + ) + ) + + return ret; +} + +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 ids_and_types[properties_out_size]; + + IKARUS_TRYRV_OR_FAIL( + , + "unable to fetch entity properties from database: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_many_buffered( + "SELECT `property`, `type` FROM `properties` WHERE `source` = ?", + 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); + + IKARUS_VTRYRV_OR_FAIL( + size_t const ret, + 0, + "unable to fetch entity property count from database: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", entity->id) + ); + + return ret; +} + +struct IkarusEntityPropertyValue * +ikarus_entity_get_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((SELECT `value` FROM `values` WHERE `entity` = ? AND `property` = ?), (SELECT `default_value` FROM `properties` WHERE `id` = ?))", + entity->id, + property->id, + property->id + ); + + IKARUS_FAIL_IF_ERROR(nullptr); + + return new IkarusEntityPropertyValue{ + .entity = entity, + .property = property, + .value = value, + }; +} + +void ikarus_entity_set_value( + IkarusEntity * entity, + struct IkarusProperty const * property, + struct IkarusEntityPropertyValue * 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, ); + + auto value_json_str = boost::json::serialize(value->value->to_json()); + + IKARUS_TRYRV_OR_FAIL( + , + "unable to set entity property value: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "INSERT INTO `values`(`entity`, `property`, `value`) VALUES(?, ?, ?) ON CONFLICT(`entity`, `property`) DO UPDATE SET `value` = ?", + entity->id, + property->id, + value_json_str, + value_json_str + ) + ); +} + +// No existence checks here for performance reasons. All methods on IkarusObject perform this check anyway. + +struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + + return static_cast(entity); +} + +struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + + return static_cast(entity); +} diff --git a/src/objects/object.cpp b/src/objects/object.cpp index 204c7a7..2f52b48 100644 --- a/src/objects/object.cpp +++ b/src/objects/object.cpp @@ -1,5 +1,109 @@ #include "object.hpp" +#include + +#include + +#include +#include +#include +#include +#include + IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): project{project}, id{id} {} + +IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(object, nullptr); + IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); + + return object->project; +} + +bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(lhs, false); + IKARUS_FAIL_IF_OBJECT_MISSING(lhs, false); + IKARUS_FAIL_IF_NULL(rhs, false); + IKARUS_FAIL_IF_OBJECT_MISSING(rhs, false); + + return lhs->id == rhs->id; +} + +void ikarus_object_visit( + IkarusObject * object, + void (*blueprint_visitor)(struct IkarusBlueprint *, void *), + void (*property_visitor)(struct IkarusProperty *, void *), + void (*entity_visitor)(struct IkarusEntity *, void *), + void * data, + IkarusErrorData * error_out +) { + struct Data { + void (*blueprint_visitor)(struct IkarusBlueprint *, void *); + void (*property_visitor)(struct IkarusProperty *, void *); + void (*entity_visitor)(struct IkarusEntity *, void *); + void * data; + }; + + Data passthru_data{ + blueprint_visitor, + property_visitor, + entity_visitor, + data, + }; + + ikarus_object_visit_const( + object, + [](IkarusBlueprint const * blueprint, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->blueprint_visitor(const_cast(blueprint), passthru_data->data); + }, + [](IkarusProperty const * property, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->property_visitor(const_cast(property), passthru_data->data); + }, + [](IkarusEntity const * entity, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->entity_visitor(const_cast(entity), passthru_data->data); + }, + &passthru_data, + error_out + ); +} + +void ikarus_object_visit_const( + IkarusObject const * object, + void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), + void (*property_visitor)(struct IkarusProperty const *, void *), + void (*entity_visitor)(struct IkarusEntity const *, void *), + void * data, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(object, ); + IKARUS_FAIL_IF_OBJECT_MISSING(object, ); + + switch (ikarus_id_get_object_type(object->id)) { + case IkarusObjectType_Entity: { + auto const * entity = dynamic_cast(object); + IKARUS_FAIL_IF(entity == nullptr, , "object with entity id wasn't a entity", IkarusErrorInfo_LibIkarus_InvalidState); + entity_visitor(entity, data); + } + case IkarusObjectType_Blueprint: { + auto const * blueprint = dynamic_cast(object); + IKARUS_FAIL_IF(blueprint == nullptr, , "object with blueprint id wasn't a blueprint", IkarusErrorInfo_LibIkarus_InvalidState); + blueprint_visitor(blueprint, data); + } + case IkarusObjectType_Property: { + auto const * property = dynamic_cast(object); + IKARUS_FAIL_IF(property == nullptr, , "object with property id wasn't a property", IkarusErrorInfo_LibIkarus_InvalidState); + property_visitor(property, data); + } + default: { + IKARUS_FAIL( + , + fmt::format("unknown object type: {}", ikarus_object_type_to_string(ikarus_id_get_object_type(object->id))), + IkarusErrorInfo_LibIkarus_InvalidState + ); + } + } +} diff --git a/src/objects/object.hpp b/src/objects/object.hpp index c51ddbb..56e141b 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -1,9 +1,5 @@ #pragma once -#include - -#include - #include struct IkarusObject { diff --git a/src/objects/properties/number_property.cpp b/src/objects/properties/number_property.cpp index d06cfb5..9d0fe13 100644 --- a/src/objects/properties/number_property.cpp +++ b/src/objects/properties/number_property.cpp @@ -1,4 +1,33 @@ #include "number_property.hpp" +#include + +#include + +#include +#include +#include + IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} + +IkarusNumberProperty * ikarus_number_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +) { + return ikarus::util::create_property(project, name, property_source, error_out); +} + +IkarusNumberValue * ikarus_number_property_get_default_value(IkarusNumberProperty * property, IkarusErrorData * error_out) { + return ikarus::util::get_default_value(property, error_out); +} + +void ikarus_number_property_set_default_value( + IkarusNumberProperty * property, + IkarusNumberValue * new_default_value, + IkarusErrorData * error_out +) { + ikarus::util::set_default_value(property, new_default_value, error_out); +} diff --git a/src/objects/properties/number_property.hpp b/src/objects/properties/number_property.hpp index df2ab7e..22ca3d3 100644 --- a/src/objects/properties/number_property.hpp +++ b/src/objects/properties/number_property.hpp @@ -1,8 +1,15 @@ #pragma once -#include +#include + +#include +#include + +struct IkarusNumberProperty : IkarusProperty { +public: + using value_type = IkarusNumberValue; + constexpr auto static PropertyType = IkarusPropertyType_Number; -struct IkarusNumberProperty final : IkarusProperty { public: IkarusNumberProperty(struct IkarusProject * project, IkarusId id); }; diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index 498e321..d3c27be 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -2,8 +2,10 @@ #include +#include #include +#include #include #include #include @@ -11,101 +13,66 @@ IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): IkarusObject{project, id}, - _data{data} {} + data{data} {} -IkarusProperty::Data & IkarusProperty::get_data() { - return _data; -} +IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(property, ); + IKARUS_FAIL_IF_OBJECT_MISSING(property, ); -IkarusProperty::Data const & IkarusProperty::get_data() const { - return _data; -} - -cppbase::Result IkarusProperty::get_property_type(IkarusProject * project, IkarusId id) { - VTRY( - auto const type, - project->db->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id).on_error([project](auto const & err) { - project->set_error( - fmt::format("failed to fetch unboxed property type: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - return cppbase::ok(static_cast(type)); -} - -IKA_API void ikarus_property_delete(IkarusProperty * property) { - TRYRV( + IKARUS_TRYRV_OR_FAIL( , - property->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([property](auto const & err) { - property->project->set_error( - fmt::format("failed to delete property from objects table: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) + "unable to delete property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", property->id) ); property->project->uncache(property); } -IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { - return IkarusProperty::get_property_type(property->project, property->id).unwrap_value_or(IkarusPropertyType_Toggle); +IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(property, IkarusPropertyType_Toggle); + IKARUS_FAIL_IF_OBJECT_MISSING(property, IkarusPropertyType_Toggle); + + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + IkarusPropertyType_Toggle, + "unable to fetch property type from database: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", property->id) + ); + + return static_cast(ret); } -IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { - VTRYRV( +IkarusPropertySource const * ikarus_property_get_source(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, - property->project->db - ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) - .on_error([property](auto const & err) { - property->project->set_error( - fmt::format("failed to fetch property's source: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) + "unable to fetch property source from database: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) ); switch (ikarus_id_get_object_type(source)) { case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->project->get_blueprint(source)}; case IkarusObjectType_Entity: return new IkarusPropertySource{property->project->get_entity(source)}; - default: { - property->project->set_error( - fmt::format("PropertySource is neither blueprint nor entity"), - true, - IkarusErrorInfo_Source_LibIkarus, - IkarusErrorInfo_Type_LibIkarus_InvalidState + default: + IKARUS_FAIL( + nullptr, + fmt::format("invalid property source type: {}", ikarus_object_type_to_string(ikarus_id_get_object_type(source))), + IkarusErrorInfo_LibIkarus_InvalidState ); - - return nullptr; - } } } -IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) { - VTRYRV( - auto const value, - nullptr, - property->project->db - ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id) - .on_error([property](auto const & err) { - property->project->set_error( - fmt::format("failed to fetch property's default value: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); +IkarusValue * 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 IkarusValue::from_json(value).unwrap_value_or(nullptr); + return fetch_value_from_db(property->project, error_out, "SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id); } void ikarus_property_visit( @@ -113,15 +80,19 @@ void ikarus_property_visit( void (*toggle_property_visitor)(struct IkarusToggleProperty *, void *), void (*number_property_visitor)(struct IkarusNumberProperty *, void *), void (*text_property_visitor)(struct IkarusTextProperty *, void *), - void * data + void * data, + IkarusErrorData * error_out ) { + 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->get_data() + property->data ); } @@ -130,18 +101,24 @@ void ikarus_property_visit_const( void (*toggle_property_visitor)(struct IkarusToggleProperty const *, void *), void (*number_property_visitor)(struct IkarusNumberProperty const *, void *), void (*text_property_visitor)(struct IkarusTextProperty const *, void *), - void * data + void * data, + IkarusErrorData * error_out ) { + 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->get_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(property); } diff --git a/src/objects/properties/property.hpp b/src/objects/properties/property.hpp index efdb515..3a95d6b 100644 --- a/src/objects/properties/property.hpp +++ b/src/objects/properties/property.hpp @@ -2,23 +2,12 @@ #include -#include - -#include - -#include - #include struct IkarusProperty : IkarusObject { public: using Data = std::variant; -public: - /// \brief Helper to fetch a type for a property that isn't yet wrapped in an object - [[nodiscard]] static cppbase::Result - get_property_type(struct IkarusProject * project, IkarusId id); - public: IkarusProperty(struct IkarusProject * project, IkarusId id, Data data); @@ -31,9 +20,5 @@ public: ~IkarusProperty() override = default; public: - [[nodiscard]] Data & get_data(); - [[nodiscard]] Data const & get_data() const; - -private: - Data _data; + Data data; }; diff --git a/src/objects/properties/property_source.cpp b/src/objects/properties/property_source.cpp index 364ca84..3e31bb3 100644 --- a/src/objects/properties/property_source.cpp +++ b/src/objects/properties/property_source.cpp @@ -3,10 +3,22 @@ #include #include +#include +#include IkarusPropertySource::IkarusPropertySource(Data data): data{data} {} +IkarusId IkarusPropertySource::get_id() const { + return boost::variant2::visit( + cppbase::overloaded { + [](IkarusBlueprint const * blueprint) { return blueprint->id; }, + [](IkarusEntity const * entity) { return entity->id; } + }, + data + ); +} + IkarusPropertySource * ikarus_property_source_create_blueprint(IkarusBlueprint * blueprint) { return new IkarusPropertySource{blueprint}; } diff --git a/src/objects/properties/property_source.hpp b/src/objects/properties/property_source.hpp index a98d664..672021d 100644 --- a/src/objects/properties/property_source.hpp +++ b/src/objects/properties/property_source.hpp @@ -1,7 +1,7 @@ #pragma once #include - +#include #include struct IkarusPropertySource { @@ -19,6 +19,9 @@ public: virtual ~IkarusPropertySource() = default; +public: + [[nodiscard]] IkarusId get_id() const; + public: Data data; }; diff --git a/src/objects/properties/text_property.cpp b/src/objects/properties/text_property.cpp index fd64154..8b3b1a5 100644 --- a/src/objects/properties/text_property.cpp +++ b/src/objects/properties/text_property.cpp @@ -1,4 +1,33 @@ #include "text_property.hpp" +#include + +#include + +#include +#include +#include + IkarusTextProperty::IkarusTextProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} + +IkarusTextProperty * ikarus_text_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +) { + return ikarus::util::create_property(project, name, property_source, error_out); +} + +IkarusTextValue * ikarus_text_property_get_default_value(IkarusTextProperty * property, IkarusErrorData * error_out) { + return ikarus::util::get_default_value(property, error_out); +} + +void ikarus_text_property_set_default_value( + IkarusTextProperty * property, + IkarusTextValue * new_default_value, + IkarusErrorData * error_out +) { + ikarus::util::set_default_value(property, new_default_value, error_out); +} diff --git a/src/objects/properties/text_property.hpp b/src/objects/properties/text_property.hpp index 3a5d163..a3b7e97 100644 --- a/src/objects/properties/text_property.hpp +++ b/src/objects/properties/text_property.hpp @@ -1,8 +1,15 @@ #pragma once -#include +#include + +#include +#include + +struct IkarusTextProperty : IkarusProperty { +public: + using value_type = IkarusTextValue; + constexpr auto static PropertyType = IkarusPropertyType_Text; -struct IkarusTextProperty final : IkarusProperty { public: IkarusTextProperty(struct IkarusProject * project, IkarusId id); }; diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp index db5105b..e0931fa 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/objects/properties/toggle_property.cpp @@ -1,7 +1,33 @@ #include "toggle_property.hpp" +#include + +#include + +#include +#include +#include + IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} -IkarusToggleProperty * -ikarus_toggle_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source) {} +IkarusToggleProperty * ikarus_toggle_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +) { + return ikarus::util::create_property(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(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(property, new_default_value, error_out); +} diff --git a/src/objects/properties/toggle_property.hpp b/src/objects/properties/toggle_property.hpp index 92bbbc4..247cbcb 100644 --- a/src/objects/properties/toggle_property.hpp +++ b/src/objects/properties/toggle_property.hpp @@ -1,8 +1,15 @@ #pragma once +#include + #include +#include struct IkarusToggleProperty : IkarusProperty { +public: + using value_type = IkarusToggleValue; + constexpr auto static PropertyType = IkarusPropertyType_Toggle; + public: IkarusToggleProperty(struct IkarusProject * project, IkarusId id); }; diff --git a/src/objects/properties/util.hpp b/src/objects/properties/util.hpp new file mode 100644 index 0000000..d44d14d --- /dev/null +++ b/src/objects/properties/util.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +namespace ikarus::util { +template +T * create_property( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF_NULL(property_source, nullptr); + IKARUS_FAIL_IF( + cppbase::is_empty_or_blank(name), + nullptr, + fmt::format("{} name cannot be empty or blank", boost::typeindex::type_id().pretty_name()), + IkarusErrorInfo_Client_InvalidInput + ) + + IKARUS_VTRYRV_OR_FAIL( + IkarusId const id, + nullptr, + "failed to create property: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->transact([name, property_source](auto * db) -> cppbase::Result { + TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Property, name)); + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Property); + TRY(db->execute( + "INSERT INTO `properties`(`id`, `type`, `source`) VALUES(?, ?, ?)", + id, + T::PropertyType, + property_source->get_id() + )); + return cppbase::ok(id); + }) + ); + + auto * ret = dynamic_cast(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().pretty_name()), + IkarusErrorInfo_LibIkarus_InvalidState + ); + + return ret; +} + +template +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(&value->data); + + IKARUS_FAIL_IF( + ret == nullptr, + nullptr, + fmt::format( + "{} default value is not a(n) {}", + boost::typeindex::type_id().pretty_name(), + boost::typeindex::type_id().pretty_name() + ), + IkarusErrorInfo_Database_InvalidState + ); + + return *ret; +} + +template +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 diff --git a/src/persistence/migrations.hpp b/src/persistence/migrations.hpp new file mode 100644 index 0000000..03390d2 --- /dev/null +++ b/src/persistence/migrations.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace ikarus { +CPPBASE_ASSET(m1_initial_layout, "persistence/migrations/m1_initial_layout.sql"); + +class Migration : public sqlitecpp::Migration { +public: + Migration(char const * sql, size_t size): + sql{sql, size} {} + + ~Migration() override = default; + +public: + [[nodiscard]] inline auto get_content() const -> std::string_view override { + return sql; + } + +public: + std::string_view sql; +}; + +#define DECLARE_MIGRATION(name) std::make_unique(static_cast(name()), name##_size()) + +constexpr std::string_view DB_VERSION_KEY = "IKARUS_DB_VERSION"; +std::vector> const MIGRATIONS = []() { + std::vector> ret; + + ret.emplace_back(DECLARE_MIGRATION(m1_initial_layout)); + + return ret; +}(); + +} // namespace ikarus diff --git a/src/persistence/migrations/m0_genesis.sql b/src/persistence/migrations/m0_genesis.sql new file mode 100644 index 0000000..952645d --- /dev/null +++ b/src/persistence/migrations/m0_genesis.sql @@ -0,0 +1,7 @@ +CREATE TABLE `metadata` +( + `key` VARCHAR(255) NOT NULL, + `value` VARCHAR(255) NOT NULL, + + PRIMARY KEY (`key`) +) \ No newline at end of file diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index da56280..7ff2019 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -1,6 +1,12 @@ #include "project.hpp" -#include "ikarus/persistence/project.h" +#include "migrations.hpp" + +#include + +#include + +#include #include #include @@ -9,10 +15,10 @@ #include #include -IkarusProject::IkarusProject(std::string_view name, std::filesystem::path path): +IkarusProject::IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db): name{name}, - path{path}, - db{nullptr}, + path{std::move(path)}, + db{std::move(db)}, _blueprints{}, _properties{}, _entities{} {} @@ -52,3 +58,222 @@ auto IkarusProject::get_property(IkarusId id, IkarusPropertyType type) -> Ikarus 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, nullptr, "unable to check whether path is occupied", 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("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(blueprints_out, ); + + if (blueprints_out_size == 0) { + return; + } + + IkarusId ids[blueprints_out_size]; + + IKARUS_TRYRV_OR_FAIL( + , + "unable to fetch project blueprints from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_many_buffered("SELECT `id` FROM `blueprints`", ids, blueprints_out_size) + ); + + for (size_t i = 0; i < blueprints_out_size; ++i) { + blueprints_out[i] = project->get_blueprint(ids[i]); + } +} + +size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, 0); + + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + 0, + "unable to fetch project blueprint count from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT COUNT(*) FROM `blueprints`") + ); + + return ret; +} + +void ikarus_project_get_entities( + IkarusProject * project, + struct IkarusEntity ** entities_out, + size_t entities_out_size, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(project, ); + IKARUS_FAIL_IF_NULL(entities_out, ); + + if (entities_out_size == 0) { + return; + } + + IkarusId ids[entities_out_size]; + + IKARUS_TRYRV_OR_FAIL( + , + "unable to fetch project entities from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_many_buffered("SELECT `id` FROM `entities`", ids, entities_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 * project, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, 0); + + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + 0, + "unable to fetch project entity count from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT COUNT(*) FROM `entities`") + ); + + return ret; +} diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index f113f24..f5a00a1 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -1,20 +1,23 @@ #pragma once -#include #include #include #include +#include + #include #include #include #include +namespace fs = boost::filesystem; + /// \private struct IkarusProject { public: - IkarusProject(std::string_view name, std::filesystem::path path); + IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db); public: [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; @@ -23,6 +26,7 @@ public: [[nodiscard]] auto get_entity(IkarusId 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(IkarusId id, IkarusPropertyType type) -> struct IkarusProperty *; auto uncache(struct IkarusProperty * property) -> void; @@ -45,11 +49,13 @@ private: public: std::string name; - std::filesystem::path path; + std::string_view path; std::unique_ptr db; private: - std::unordered_map> _blueprints; - std::unordered_map> _properties; - std::unordered_map> _entities; + std::unordered_map> mutable _blueprints; + std::unordered_map> mutable _properties; + std::unordered_map> mutable _entities; }; + +constexpr std::string_view DB_PROJECT_NAME_KEY = "PROJECT_NAME"; diff --git a/src/values/entity_property_value.cpp b/src/values/entity_property_value.cpp new file mode 100644 index 0000000..d1580ea --- /dev/null +++ b/src/values/entity_property_value.cpp @@ -0,0 +1,21 @@ +#include "values/entity_property_value.hpp" + +#include + +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; +} diff --git a/src/values/entity_property_value.hpp b/src/values/entity_property_value.hpp new file mode 100644 index 0000000..f75d6ba --- /dev/null +++ b/src/values/entity_property_value.hpp @@ -0,0 +1,7 @@ +#pragma once + +struct IkarusEntityPropertyValue { + struct IkarusEntity const * entity; + struct IkarusProperty const * property; + struct IkarusValue const * value; +}; diff --git a/src/values/value.cpp b/src/values/value.cpp index cf9a270..812b2c8 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -19,7 +19,7 @@ IkarusValue::IkarusValue(Data data): data(data) {} -cppbase::Result IkarusValue::from_json(boost::json::value const & json) { +cppbase::Result IkarusValue::from_json(boost::json::value json) { if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); } else { diff --git a/src/values/value.hpp b/src/values/value.hpp index adc27ca..94b423d 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -5,6 +5,9 @@ #include +#include +#include + struct IkarusValue { public: using Data = boost::variant2::variant; @@ -24,9 +27,50 @@ public: public: struct FromJsonError {}; - [[nodiscard]] static cppbase::Result from_json(boost::json::value const & json); + [[nodiscard]] static cppbase::Result from_json(boost::json::value json); [[nodiscard]] boost::json::value to_json() const; public: Data data; }; + +template<> +struct fmt::formatter { + template + constexpr static auto parse(FormatParseContext & ctx) { + return ctx.end(); + } + + constexpr static auto format([[maybe_unused]]IkarusValue::FromJsonError const & error, fmt::format_context & ctx) { + return fmt::format_to(ctx.out(), "unable to parse ikarus value JSON"); + } +}; + +template +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(query, std::forward(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; +} diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 00a1afc..e837d64 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 00a1afcc5f564f562c436f1ddfa4f44bb6489b17 +Subproject commit e837d64405fb43adefe03994837f5ed5793138c5 From a28b3d50af2967ab5d237b312908e9708c1a7100 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 7 Jan 2024 14:32:44 +0100 Subject: [PATCH 036/166] export compile commands Signed-off-by: Folling --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8389d4f..de1a1a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,8 @@ set(CMAKE_CXX_STANDARD 23) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + add_subdirectory(vendor) add_subdirectory(include) add_subdirectory(src) From 9b0652910a0019cbd8750bab01e7275b588cf3e4 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 13 Jan 2024 15:54:26 +0100 Subject: [PATCH 037/166] rename libikarus => ikarus in cmake & update sqlitecpp Signed-off-by: Folling --- CMakeLists.txt | 18 +++++++++--------- vendor/sqlitecpp | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de1a1a9..c5abd8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,30 +19,30 @@ add_subdirectory(src) find_package(Boost COMPONENTS system filesystem REQUIRED) add_library( - libikarus SHARED + ikarus SHARED ${INCLUDE_FILES} ${SOURCE_FILES} ) target_include_directories( - libikarus PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/include + ikarus PUBLIC + $ ) target_include_directories( - libikarus PRIVATE + ikarus PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src ) target_link_libraries( - libikarus PRIVATE + ikarus PRIVATE cppbase sqlitecpp ${Boost_LIBRARIES} ) target_include_directories( - libikarus PRIVATE + ikarus PRIVATE ${Boost_INCLUDE_DIRS} ) @@ -50,9 +50,9 @@ if (LIBIKARUS_ENABLE_LINTS) find_program(IWYU_PATH NAMES include-what-you-use iwyu REQUIRED) find_program(CLANG_TIDY_PATH NAMES clang-tidy REQUIRED) - set_property(TARGET libikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) + set_property(TARGET ikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) set_property( - TARGET libikarus + TARGET ikarus PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY_PATH}; ) endif () @@ -83,7 +83,7 @@ if (LIBIKARUS_BUILD_DOCS) ) add_dependencies( - libikarus + ikarus libikarus_docs ) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index e837d64..e41d51e 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit e837d64405fb43adefe03994837f5ed5793138c5 +Subproject commit e41d51ed750c5f0a37fc1894c5dd382df32ead26 From 3c250702b9b83c94baeb1194b7ea302d8885cd13 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 13 Jan 2024 16:04:03 +0100 Subject: [PATCH 038/166] fix missing include Signed-off-by: Folling --- CMakeLists.txt | 2 +- include/ikarus/persistence/project.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5abd8f..01e5f44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ add_library( target_include_directories( ikarus PUBLIC - $ + ${CMAKE_CURRENT_LIST_DIR}/include ) target_include_directories( diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index c4406f8..f08107b 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -3,6 +3,7 @@ /// \file project.h /// \author Folling +#include #include #include From c289344880050b5171ea8c7c6c76d9bb64fd0cfc Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 13 Jan 2024 23:46:33 +0100 Subject: [PATCH 039/166] fix various issues Signed-off-by: Folling --- include/ikarus/persistence/project.h | 2 +- include/ikarus/stdtypes.h | 2 +- src/global.cpp | 7 +++ src/id.cpp | 2 - src/objects/blueprint.cpp | 2 +- src/objects/entity.cpp | 2 +- src/objects/properties/util.hpp | 2 +- src/persistence/migrations.hpp | 2 +- src/persistence/migrations/m0_genesis.sql | 7 --- ...itial_layout.sql => m0_initial_layout.sql} | 45 +++++++------------ src/persistence/project.cpp | 8 ++-- vendor/sqlitecpp | 2 +- 12 files changed, 33 insertions(+), 50 deletions(-) create mode 100644 src/global.cpp delete mode 100644 src/persistence/migrations/m0_genesis.sql rename src/persistence/migrations/{m1_initial_layout.sql => m0_initial_layout.sql} (75%) diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index f08107b..117909e 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -101,7 +101,7 @@ IKA_API void ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Gets the entities of a project. /// \param project The project to get the entities of. diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h index 6fa7e95..4274e3f 100644 --- a/include/ikarus/stdtypes.h +++ b/include/ikarus/stdtypes.h @@ -3,8 +3,8 @@ #ifdef __cplusplus #include #include -// NOLINTNEXTLINE(google-global-names-in-headers) using std::size_t; #else +#include #include #endif diff --git a/src/global.cpp b/src/global.cpp new file mode 100644 index 0000000..8b1929a --- /dev/null +++ b/src/global.cpp @@ -0,0 +1,7 @@ +#include "ikarus/global.h" + +#include + +void ikarus_free(void * ptr) { + std::free(ptr); +} \ No newline at end of file diff --git a/src/id.cpp b/src/id.cpp index 463aa3d..031c490 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -1,7 +1,5 @@ #include "ikarus/id.h" -#include - #include constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index 971bd13..e5f3f0c 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -26,7 +26,7 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c "failed to create blueprint: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Blueprint, name)); + TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Blueprint, name, "")); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id)); return cppbase::ok(id); diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp index 174123e..6c1839a 100644 --- a/src/objects/entity.cpp +++ b/src/objects/entity.cpp @@ -21,7 +21,7 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * "failed to create entity: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Entity, name)); + TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Entity, name, "")); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Entity); TRY(db->execute("INSERT INTO `entities`(`id`) VALUES(?)", id)); return cppbase::ok(id); diff --git a/src/objects/properties/util.hpp b/src/objects/properties/util.hpp index d44d14d..2e11475 100644 --- a/src/objects/properties/util.hpp +++ b/src/objects/properties/util.hpp @@ -36,7 +36,7 @@ T * create_property( "failed to create property: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name, property_source](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Property, name)); + TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Property, name, "")); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Property); TRY(db->execute( "INSERT INTO `properties`(`id`, `type`, `source`) VALUES(?, ?, ?)", diff --git a/src/persistence/migrations.hpp b/src/persistence/migrations.hpp index 03390d2..3416ffb 100644 --- a/src/persistence/migrations.hpp +++ b/src/persistence/migrations.hpp @@ -8,7 +8,7 @@ #include namespace ikarus { -CPPBASE_ASSET(m1_initial_layout, "persistence/migrations/m1_initial_layout.sql"); +CPPBASE_ASSET(m0_initial_layout, "persistence/migrations/m0_initial_layout.sql"); class Migration : public sqlitecpp::Migration { public: diff --git a/src/persistence/migrations/m0_genesis.sql b/src/persistence/migrations/m0_genesis.sql deleted file mode 100644 index 952645d..0000000 --- a/src/persistence/migrations/m0_genesis.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE `metadata` -( - `key` VARCHAR(255) NOT NULL, - `value` VARCHAR(255) NOT NULL, - - PRIMARY KEY (`key`) -) \ No newline at end of file diff --git a/src/persistence/migrations/m1_initial_layout.sql b/src/persistence/migrations/m0_initial_layout.sql similarity index 75% rename from src/persistence/migrations/m1_initial_layout.sql rename to src/persistence/migrations/m0_initial_layout.sql index ea24c56..99ba61b 100644 --- a/src/persistence/migrations/m1_initial_layout.sql +++ b/src/persistence/migrations/m0_initial_layout.sql @@ -1,27 +1,22 @@ CREATE TABLE `objects` ( `do_not_access_rowid_alias` INTEGER PRIMARY KEY, - `object_type` INT NOT NULL, - `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56) -) VIRTUAL, + `type` INT NOT NULL, + `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`type` << 56)) VIRTUAL, `name` TEXT NOT NULL, `information` TEXT NOT NULL ) STRICT; CREATE UNIQUE INDEX `object_id` ON `objects` (`id`); -CREATE INDEX `object_type` ON `objects` (`object_type`); +CREATE INDEX `object_type` ON `objects` (`type`); -CREATE -VIRTUAL TABLE `objects_fts` USING fts5 +CREATE VIRTUAL TABLE `objects_fts` USING fts5 ( `name`, `information`, - content= - 'objects', - content_rowid= - 'id', - tokenize= - "unicode61 remove_diacritics 2 tokenchars '-_'" + content='objects', + content_rowid='id', + tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" ); CREATE TABLE `entities` @@ -73,24 +68,18 @@ CREATE VIRTUAL TABLE `property_default_value_fts` USING fts5 ( `default_value`, - content= - 'properties', - content_rowid= - 'object_id', - tokenize= - "unicode61 remove_diacritics 2 tokenchars '-_'" + content='properties', + content_rowid='object_id', + tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" ); CREATE VIRTUAL TABLE `property_settings_fts` USING fts5 ( `settings`, - content= - 'properties', - content_rowid= - 'object_id', - tokenize= - "unicode61 remove_diacritics 2 tokenchars '-_'" + content='properties', + content_rowid='object_id', + tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" ); CREATE TABLE `values` @@ -108,8 +97,6 @@ CREATE VIRTUAL TABLE `values_fts` USING fts5 ( `value`, - content= - 'values', - tokenize= - "unicode61 remove_diacritics 2 tokenchars '-_'" -) + content='values', + tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" +); diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index 7ff2019..41ddbf7 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -1,19 +1,17 @@ -#include "project.hpp" - -#include "migrations.hpp" +#include "ikarus/persistence/project.h" #include #include -#include - #include #include #include #include #include #include +#include +#include IkarusProject::IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db): name{name}, diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index e41d51e..66f5747 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit e41d51ed750c5f0a37fc1894c5dd382df32ead26 +Subproject commit 66f5747f1ad3e43960e9a77755e770229b90004e From 0b3b49b6feb95fe08efc5551d2d40b23cdf32553 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 13 Jan 2024 23:56:30 +0100 Subject: [PATCH 040/166] update sqlitecpp Signed-off-by: Folling --- src/persistence/migrations.hpp | 2 +- vendor/sqlitecpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/persistence/migrations.hpp b/src/persistence/migrations.hpp index 3416ffb..c777e84 100644 --- a/src/persistence/migrations.hpp +++ b/src/persistence/migrations.hpp @@ -32,7 +32,7 @@ constexpr std::string_view DB_VERSION_KEY = "IKARUS_DB_VERSION"; std::vector> const MIGRATIONS = []() { std::vector> ret; - ret.emplace_back(DECLARE_MIGRATION(m1_initial_layout)); + ret.emplace_back(DECLARE_MIGRATION(m0_initial_layout)); return ret; }(); diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 66f5747..f27ef69 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 66f5747f1ad3e43960e9a77755e770229b90004e +Subproject commit f27ef69131166549e9c250b658bf26136b09208b From 779afc433355c1c543cc26cad5f6e21a2bbf8932 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 14 Jan 2024 00:12:52 +0100 Subject: [PATCH 041/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index f27ef69..eadf323 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit f27ef69131166549e9c250b658bf26136b09208b +Subproject commit eadf3237bfa853763b332777e9dc0f16df8cca71 From dc3537344abb681c8114c538ce9bcb4144264935 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 14 Jan 2024 02:11:13 +0100 Subject: [PATCH 042/166] add object_get/set_name/information Signed-off-by: Folling --- include/ikarus/objects/object.h | 38 ++++++++++++++++++++ src/objects/object.cpp | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index 9dd3b39..7dcc97f 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -22,17 +22,55 @@ struct IkarusObject; /// \brief Fetches the project of an object. /// \param object The object to fetch the project from. /// \pre \li Must not be null. +/// \pre \li Must exist. /// \param error_out \see errors.h /// \return The project of the object or null if an error occurs. IKA_API struct IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErrorData * error_out); +/// \brief Fetches the name of an object. +/// \param object The object to fetch the name from. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The name of the object or null if an error occurs. +IKA_API char const * ikarus_object_get_name(IkarusObject const * object, IkarusErrorData * error_out); + +/// \brief Sets the name of an object. +/// \param object The object to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the object. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param error_out \see errors.h +IKA_API void ikarus_object_set_name(IkarusObject const * object, char const * new_name, IkarusErrorData * error_out); + +/// \brief Fetches the information of an object. +/// \param object The object to fetch the information from. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The information of the object or null if an error occurs. +IKA_API char const * ikarus_object_get_info(IkarusObject const * object, IkarusErrorData * error_out); + +/// \brief Sets the information of an object. +/// \param object The object to set the information of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_info The new information of the object. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +IKA_API void ikarus_object_set_info(IkarusObject const * object, char const * new_info, IkarusErrorData * error_out); + /// \brief Compares two objects for equality. /// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the /// same object. /// \param lhs The left hand side object. /// \pre \li Must not be null. +/// \pre \li Must exist. /// \param rhs The right hand side object. /// \pre \li Must not be null. +/// \pre \li Must exist. /// \param error_out \see errors.h /// \return True if the objects are equal, false otherwise. IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out); diff --git a/src/objects/object.cpp b/src/objects/object.cpp index 2f52b48..e8a3353 100644 --- a/src/objects/object.cpp +++ b/src/objects/object.cpp @@ -2,6 +2,9 @@ #include +#include + +#include #include #include @@ -21,6 +24,64 @@ IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErr return object->project; } +char const * ikarus_object_get_name(IkarusObject const * object, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(object, nullptr); + IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + std::string ret, + nullptr, + "unable to get object name: {}", + IkarusErrorInfo_Database_QueryFailed, + object->project->db->template query_one("SELECT `name` FROM `objects` WHERE `id` = ?", object->id) + ); + + return strdup(ret.data()); +} + +void ikarus_object_set_name(IkarusObject * 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(cppbase::is_empty_or_blank(name), , "name must not be empty", IkarusErrorInfo_Client_InvalidInput); + + IKARUS_TRYRV_OR_FAIL( + , + "unable to set object name: {}", + IkarusErrorInfo_Database_QueryFailed, + object->project->db->template execute("UPDATE `objects` SET `name` = ? WHERE `id` = ?", name, object->id) + ); +} + +char const * ikarus_object_get_information(IkarusObject const * object, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(object, nullptr); + IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + std::string ret, + nullptr, + "unable to get object information: {}", + IkarusErrorInfo_Database_QueryFailed, + object->project->db->template query_one("SELECT `information` FROM `objects` WHERE `id` = ?", object->id) + ); + + return strdup(ret.data()); +} + +void ikarus_object_set_information(IkarusObject * object, char const * information, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(object, ); + IKARUS_FAIL_IF_OBJECT_MISSING(object, ); + IKARUS_FAIL_IF_NULL(information, ); + IKARUS_FAIL_IF(cppbase::is_empty_or_blank(information), , "information must not be empty", IkarusErrorInfo_Client_InvalidInput); + + IKARUS_TRYRV_OR_FAIL( + , + "unable to set object information: {}", + IkarusErrorInfo_Database_QueryFailed, + object->project->db->template execute("UPDATE `objects` SET `information` = ? WHERE `id` = ?", information, object->id) + ); +} + bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(lhs, false); IKARUS_FAIL_IF_OBJECT_MISSING(lhs, false); From a9d9503d82b2ca73d48f787ffa5254ea769d8262 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 14 Jan 2024 13:46:07 +0100 Subject: [PATCH 043/166] fixup id generation Signed-off-by: Folling --- src/id.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/id.cpp b/src/id.cpp index 031c490..4269215 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -3,7 +3,7 @@ #include constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; -constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; +constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; auto ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); From c65211e4ff67193788de6d07366956537f373892 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 15 Jan 2024 03:37:26 +0100 Subject: [PATCH 044/166] add default values as param to property_create funcs Signed-off-by: Folling --- include/ikarus/objects/properties/number_property.h | 3 +++ include/ikarus/objects/properties/text_property.h | 3 +++ include/ikarus/objects/properties/toggle_property.h | 3 +++ src/objects/properties/number_property.cpp | 6 ++++-- src/objects/properties/text_property.cpp | 3 ++- src/objects/properties/toggle_property.cpp | 3 ++- src/objects/properties/util.hpp | 9 ++++++--- vendor/sqlitecpp | 2 +- 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 8ca4ffd..2fcf63b 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -24,12 +24,15 @@ struct IkarusNumberProperty; /// \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 ); diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index 0408614..5e59d92 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -24,12 +24,15 @@ struct IkarusTextProperty; /// \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 ); diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index 892d418..403f392 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -24,12 +24,15 @@ struct IkarusToggleProperty; /// \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 IkarusToggleProperty * ikarus_toggle_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, + struct IkarusToggleValue * default_value, IkarusErrorData * error_out ); diff --git a/src/objects/properties/number_property.cpp b/src/objects/properties/number_property.cpp index 9d0fe13..b94429d 100644 --- a/src/objects/properties/number_property.cpp +++ b/src/objects/properties/number_property.cpp @@ -1,9 +1,10 @@ -#include "number_property.hpp" +#include "ikarus/objects/properties/number_property.h" #include #include +#include #include #include #include @@ -15,9 +16,10 @@ IkarusNumberProperty * ikarus_number_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, + struct IkarusNumberValue * default_value, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, error_out); + return ikarus::util::create_property(project, name, property_source, default_value, error_out); } IkarusNumberValue * ikarus_number_property_get_default_value(IkarusNumberProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/text_property.cpp b/src/objects/properties/text_property.cpp index 8b3b1a5..c7541ff 100644 --- a/src/objects/properties/text_property.cpp +++ b/src/objects/properties/text_property.cpp @@ -15,9 +15,10 @@ IkarusTextProperty * ikarus_text_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, + struct IkarusTextValue * default_value, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, error_out); + return ikarus::util::create_property(project, name, property_source, default_value, error_out); } IkarusTextValue * ikarus_text_property_get_default_value(IkarusTextProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp index e0931fa..2cde654 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/objects/properties/toggle_property.cpp @@ -15,9 +15,10 @@ IkarusToggleProperty * ikarus_toggle_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, + struct IkarusToggleValue * default_value, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, error_out); + return ikarus::util::create_property(project, name, property_source, default_value, error_out); } IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/util.hpp b/src/objects/properties/util.hpp index 2e11475..d9321c7 100644 --- a/src/objects/properties/util.hpp +++ b/src/objects/properties/util.hpp @@ -18,6 +18,7 @@ T * create_property( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, + typename T::value_type * default_value, IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(project, nullptr); @@ -29,20 +30,22 @@ T * create_property( fmt::format("{} name cannot be empty or blank", boost::typeindex::type_id().pretty_name()), IkarusErrorInfo_Client_InvalidInput ) + IKARUS_FAIL_IF_NULL(default_value, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, nullptr, "failed to create property: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->transact([name, property_source](auto * db) -> cppbase::Result { + project->db->transact([name, property_source, default_value](auto * db) -> cppbase::Result { TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Property, name, "")); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Property); TRY(db->execute( - "INSERT INTO `properties`(`id`, `type`, `source`) VALUES(?, ?, ?)", + "INSERT INTO `properties`(`id`, `type`, `source`, `default_value`) VALUES(?, ?, ?, ?)", id, T::PropertyType, - property_source->get_id() + property_source->get_id(), + boost::json::serialize(default_value->to_json()) )); return cppbase::ok(id); }) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index eadf323..77e7260 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit eadf3237bfa853763b332777e9dc0f16df8cca71 +Subproject commit 77e726036b52fff6f82ff2d44081f05aee7408a8 From 5dce2ced9464fe82f7e07239ad4e93019b987b9e Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 30 Jan 2024 09:31:08 +0100 Subject: [PATCH 045/166] add src/ikarus subdir and make names unique for objects per scope Signed-off-by: Folling --- .clang-tidy | 26 ++- clang-format.txt | 192 ------------------ include/ikarus/errors.h | 3 +- include/ikarus/id.h | 8 +- include/ikarus/objects/blueprint.h | 41 ++-- include/ikarus/objects/entity.h | 41 ++-- include/ikarus/objects/object.h | 68 +------ include/ikarus/objects/properties/property.h | 3 +- .../{property_source.h => property_scope.h} | 10 +- include/ikarus/persistence/project.h | 88 +++++--- src/{ => ikarus}/errors.cpp | 4 +- src/{ => ikarus}/errors.hpp | 0 src/{ => ikarus}/global.cpp | 0 src/{ => ikarus}/id.cpp | 2 +- src/{ => ikarus}/objects/blueprint.cpp | 43 ++-- src/{ => ikarus}/objects/blueprint.hpp | 8 +- src/{ => ikarus}/objects/entity.cpp | 47 ++--- src/{ => ikarus}/objects/entity.hpp | 8 +- src/ikarus/objects/object.cpp | 93 +++++++++ src/{ => ikarus}/objects/object.hpp | 5 + src/{ => ikarus}/objects/object_type.cpp | 0 .../objects/properties/number_property.cpp | 15 +- .../objects/properties/number_property.hpp | 5 +- .../objects/properties/property.cpp | 29 ++- .../objects/properties/property.hpp | 7 +- .../objects/properties/property_scope.cpp} | 39 ++-- .../objects/properties/property_scope.hpp | 39 ++++ .../objects/properties/text_property.cpp | 12 +- .../objects/properties/text_property.hpp | 5 +- .../objects/properties/toggle_property.cpp | 12 +- .../objects/properties/toggle_property.hpp | 5 +- src/{ => ikarus}/objects/properties/util.hpp | 34 ++-- src/ikarus/objects/util.hpp | 125 ++++++++++++ src/{ => ikarus}/persistence/migrations.hpp | 2 +- .../migrations/m0_initial_layout.sql | 54 +---- src/{ => ikarus}/persistence/project.cpp | 16 +- src/{ => ikarus}/persistence/project.hpp | 0 .../values/entity_property_value.cpp | 4 +- .../values/entity_property_value.hpp | 0 src/{ => ikarus}/values/number_value.cpp | 4 +- src/{ => ikarus}/values/number_value.hpp | 2 +- src/{ => ikarus}/values/text_value.cpp | 4 +- src/{ => ikarus}/values/text_value.hpp | 2 +- src/{ => ikarus}/values/toggle_value.cpp | 4 +- src/{ => ikarus}/values/toggle_value.hpp | 2 +- src/{ => ikarus}/values/value.cpp | 9 +- src/{ => ikarus}/values/value.hpp | 6 +- src/{ => ikarus}/values/value_base.hpp | 0 src/objects/object.cpp | 170 ---------------- src/objects/properties/property_source.hpp | 27 --- vendor/sqlitecpp | 2 +- 51 files changed, 590 insertions(+), 735 deletions(-) delete mode 100644 clang-format.txt rename include/ikarus/objects/properties/{property_source.h => property_scope.h} (86%) rename src/{ => ikarus}/errors.cpp (98%) rename src/{ => ikarus}/errors.hpp (100%) rename src/{ => ikarus}/global.cpp (100%) rename src/{ => ikarus}/id.cpp (93%) rename src/{ => ikarus}/objects/blueprint.cpp (83%) rename src/{ => ikarus}/objects/blueprint.hpp (71%) rename src/{ => ikarus}/objects/entity.cpp (88%) rename src/{ => ikarus}/objects/entity.hpp (72%) create mode 100644 src/ikarus/objects/object.cpp rename src/{ => ikarus}/objects/object.hpp (82%) rename src/{ => ikarus}/objects/object_type.cpp (100%) rename src/{ => ikarus}/objects/properties/number_property.cpp (70%) rename src/{ => ikarus}/objects/properties/number_property.hpp (77%) rename src/{ => ikarus}/objects/properties/property.cpp (81%) rename src/{ => ikarus}/objects/properties/property.hpp (79%) rename src/{objects/properties/property_source.cpp => ikarus/objects/properties/property_scope.cpp} (58%) create mode 100644 src/ikarus/objects/properties/property_scope.hpp rename src/{ => ikarus}/objects/properties/text_property.cpp (77%) rename src/{ => ikarus}/objects/properties/text_property.hpp (77%) rename src/{ => ikarus}/objects/properties/toggle_property.cpp (77%) rename src/{ => ikarus}/objects/properties/toggle_property.hpp (77%) rename src/{ => ikarus}/objects/properties/util.hpp (73%) create mode 100644 src/ikarus/objects/util.hpp rename src/{ => ikarus}/persistence/migrations.hpp (90%) rename src/{ => ikarus}/persistence/migrations/m0_initial_layout.sql (61%) rename src/{ => ikarus}/persistence/project.cpp (95%) rename src/{ => ikarus}/persistence/project.hpp (100%) rename src/{ => ikarus}/values/entity_property_value.cpp (90%) rename src/{ => ikarus}/values/entity_property_value.hpp (100%) rename src/{ => ikarus}/values/number_value.cpp (96%) rename src/{ => ikarus}/values/number_value.hpp (94%) rename src/{ => ikarus}/values/text_value.cpp (96%) rename src/{ => ikarus}/values/text_value.hpp (94%) rename src/{ => ikarus}/values/toggle_value.cpp (96%) rename src/{ => ikarus}/values/toggle_value.hpp (94%) rename src/{ => ikarus}/values/value.cpp (96%) rename src/{ => ikarus}/values/value.hpp (91%) rename src/{ => ikarus}/values/value_base.hpp (100%) delete mode 100644 src/objects/object.cpp delete mode 100644 src/objects/properties/property_source.hpp diff --git a/.clang-tidy b/.clang-tidy index 63731e9..49b97c2 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,26 +1,24 @@ Checks: >- -*, - bugprone-*, -bugprone-lambda-function-name, - cppcoreguidelines-*, -cppcoreguidelines-macro-usage, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes, - clang-analyzer-*, - google-*, -google-readability-todo, - modernize-*, -modernize-use-trailing-return-type, - performance-*, -performance-enum-size, - portability-*, - readability-*, -readability-redundant-access-specifiers +# bugprone-*, -bugprone-lambda-function-name, +# cppcoreguidelines-*, -cppcoreguidelines-macro-usage, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes, +# clang-analyzer-*, +# google-*, -google-readability-todo, +# modernize-*, -modernize-use-trailing-return-type, +# performance-*, -performance-enum-size, +# portability-*, +# readability-*, -readability-redundant-access-specifiers CheckOptions: readability-identifier-length.IgnoredParameterNames: '^(db|rc|id|ec)$' readability-identifier-length.IgnoredLoopCounterNames: '^[ij]$' readability-identifier-length.IgnoredVariableNames: '^(db|rc|id|ec)$' cppcoreguidelines-avoid-do-while.IgnoreMacros: Yes HeaderFileExtensions: - - h - - hpp - - tpp - - ipp +# - hpp +# - tpp +# - ipp ImplementationFileExtensions: - - c - - cpp +# - cpp FormatStyle: file InheritParentConfig: false WarningsAsErrors: '*' diff --git a/clang-format.txt b/clang-format.txt deleted file mode 100644 index a600edb..0000000 --- a/clang-format.txt +++ /dev/null @@ -1,192 +0,0 @@ -BasedOnStyle: Google - -AccessModifierOffset: -4 - -AlignAfterOpenBracket: BlockIndent -AlignArrayOfStructures: Right -AlignConsecutiveAssignments: - Enabled: false -AlignConsecutiveBitFields: - Enabled: false -AlignConsecutiveDeclarations: - Enabled: false -AlignConsecutiveMacros: AcrossEmptyLines -AlignEscapedNewlines: Left -AlignOperands: Align -AlignTrailingComments: true - -AllowAllArgumentsOnNextLine: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: Empty -AllowShortCaseLabelsOnASingleLine: true -AllowShortEnumsOnASingleLine: true -AllowShortFunctionsOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortLambdasOnASingleLine: All -AllowShortLoopsOnASingleLine: true - -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: Yes - -BinPackArguments: false -BinPackParameters: false - -BitFieldColonSpacing: Both - -BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyNamespace: false - SplitEmptyRecord: false - -BracedInitializerIndentWidth: 4 - -# BreakAdjacentStringLiterals: true -BreakAfterAttributes: Never -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Custom -BreakBeforeConceptDeclarations: Always -BreakBeforeInlineASMColon: OnlyMultiline -BreakBeforeTernaryOperators: false -BreakConstructorInitializers: AfterColon -BreakInheritanceList: AfterColon -BreakStringLiterals: false - -ColumnLimit: 140 - -CompactNamespaces: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 - -Cpp11BracedListStyle: true - -DerivePointerAlignment: false - -EmptyLineAfterAccessModifier: Never -EmptyLineBeforeAccessModifier: Always - -ExperimentalAutoDetectBinPacking: true - -FixNamespaceComments: true - -IncludeBlocks: Regroup -IncludeCategories: - - Regex: '^".+\.(h|hpp)"$' - Priority: 1 - - Regex: '^<[a-z0-9_]+\.h>$' - Priority: 2 - - Regex: '^<[a-z0-9_]+>$' - Priority: 3 - - Regex: '^$' - Priority: 4 - - Regex: '^$' - Priority: 5 - - Regex: '^$' - Priority: 6 - - Regex: '^$' - Priority: 7 - - Regex: '^$' - Priority: 8 - - Regex: '^$' - Priority: 9 - - Regex: '^$' - Priority: 10 - - Regex: '^$' - Priority: 11 - - Regex: '^$' - Priority: 12 - - Regex: '^$' - Priority: 13 - -IndentAccessModifiers: false -IndentCaseBlocks: false -IndentCaseLabels: false -IndentExternBlock: NoIndent -IndentGotoLabels: false -IndentPPDirectives: None -IndentRequiresClause: true -IndentWidth: 4 -IndentWrappedFunctionNames: false -InsertBraces: true -InsertNewlineAtEOF: true -InsertTrailingCommas: Wrapped - -IntegerLiteralSeparator: - Binary: -1 - Decimal: 3 - Hex: -1 - -KeepEmptyLinesAtEOF: false -KeepEmptyLinesAtTheStartOfBlocks: false - -LambdaBodyIndentation: Signature -Language: Cpp - -LineEnding: LF - -MaxEmptyLinesToKeep: 1 - -NamespaceIndentation: None - -PPIndentWidth: -1 -PackConstructorInitializers: Never - -PointerAlignment: Middle -QualifierAlignment: Right -# QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ] -ReferenceAlignment: Middle - -ReflowComments: true -RemoveBracesLLVM: false -RemoveParentheses: MultipleParentheses -RemoveSemicolon: true - -RequiresClausePosition: OwnLine -RequiresExpressionIndentation: OuterScope - -SeparateDefinitionBlocks: Always - -SortIncludes: CaseInsensitive -SortUsingDeclarations: LexicographicNumeric - -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: false -SpaceAroundPointerQualifiers: Both -SpaceBeforeAssignmentOperators: true -SpaceBeforeCaseColon: false -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: false -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: true -SpaceBeforeSquareBrackets: false -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesInAngles: false -SpacesInCStyleCastParentheses: false -SpacesInConditionalStatement: false -SpacesInContainerLiterals: false -SpacesInLineCommentPrefix: - Minimum: 1 - Maximum: 1 -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: c++20 - -TabWidth: 4 -UseTab: Never diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index a4887a3..ce11788 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -1,6 +1,6 @@ #pragma once -/// \file global.h +/// \file errors.h /// \author Folling #include @@ -92,6 +92,7 @@ enum IkarusErrorInfo { IkarusErrorInfo_LibIkarus_Timeout = 0x06000003, }; +/// \brief The maximum length of an error message. size_t const IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT = 128; /// \brief The data stored for an error diff --git a/include/ikarus/id.h b/include/ikarus/id.h index ae545a9..c76e341 100644 --- a/include/ikarus/id.h +++ b/include/ikarus/id.h @@ -11,8 +11,6 @@ #include #include -IKARUS_BEGIN_HEADER - /// \defgroup id Ids /// \brief Ids are used to identify objects in the database. /// \details They are stored as 64 bit integers with the following layout: @@ -22,6 +20,8 @@ IKARUS_BEGIN_HEADER /// - last 56 bits: incremented counter generated by the database /// @{ +IKARUS_BEGIN_HEADER + /// \brief A wrapper around a 64 bit integer that represents the id of an object. /// \details They are stored as 64 bit integers with the following layout: /// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. @@ -41,6 +41,6 @@ IKA_API IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType typ /// \return The object type of the given id. IKA_API IkarusObjectType ikarus_id_get_object_type(IkarusId id); -/// @} - IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 3ce07ce..adfacd4 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -25,9 +25,9 @@ struct IkarusBlueprint; /// \param name The name of the blueprint. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \pre \li Must be unique among all blueprints in the project. /// \param error_out \see errors.h /// \return The created blueprint or null if an error occurs. -/// \remark Must be freed using #ikarus_free. IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Deletes & frees a blueprint. @@ -38,6 +38,33 @@ IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project /// \remark The blueprint must not be accessed after deletion. IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * error_out); +/// \brief Gets the project a blueprint is part of. +/// \param blueprint The blueprint 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 blueprint is part of or null if an error occurs. +IKA_API IkarusProject * ikarus_blueprint_get_project(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); + +/// \brief Gets the name of a blueprint. +/// \param blueprint The blueprint 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 blueprint or null if an error occurs. +IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); + +/// \brief Sets the name of a blueprint. +/// \param blueprint The blueprint to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The new name of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \pre \li Must be unique among all blueprints in the project. +/// \param error_out \see errors.h +IKA_API void ikarus_blueprint_set_name(IkarusBlueprint * blueprint, char const * name, IkarusErrorData * error_out); + /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. /// \pre \li Must not be null. @@ -86,18 +113,6 @@ IKA_API void ikarus_blueprint_get_linked_entities( /// \return The number of linked entities or undefined if an error occurs. IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); -/// \brief Casts a blueprint to an object. -/// \param blueprint The blueprint to cast. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The blueprint represented as an object or null if an error occurs. -/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint, IkarusErrorData * error_out); - -/// \see ikarus_blueprint_to_object -IKA_API struct IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); - IKARUS_END_HEADER // @} diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 9610230..53abc70 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -41,9 +41,9 @@ struct IkarusEntity; /// \param name The name of the entity. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \pre \li Must be unique among all entities in the project. /// \param error_out \see errors.h /// \return The created entity or null if an error occurs. -/// \remark Must be freed using #ikarus_free. IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Deletes an entity. @@ -54,6 +54,33 @@ IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char /// \remark The entity must not be accessed after deletion. IKA_API void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); +/// \brief Gets the project an entity is part of. +/// \param entity The entity 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 entity is part of or null if an error occurs. +IKA_API IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity, IkarusErrorData * error_out); + +/// \brief Gets the name of an entity. +/// \param entity The entity 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 entity or null if an error occurs. +IKA_API char const * ikarus_entity_get_name(IkarusEntity const * entity, IkarusErrorData * error_out); + +/// \brief Sets the name of an entity. +/// \param entity The entity to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The new name of the entity. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \pre \li Must be unique among all entities in the project. +/// \param error_out \see errors.h +IKA_API void ikarus_entity_set_name(IkarusEntity * entity, char const * name, IkarusErrorData * error_out); + /// \brief Checks if an entity is linked to a blueprint. /// \param entity The entity to check. /// \pre \li Must not be null. @@ -182,18 +209,6 @@ IKA_API void ikarus_entity_set_value( IkarusErrorData * error_out ); -/// \brief Casts an entity to an object. -/// \param entity The entity to cast. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The entity represented as an object or null if an error occurs. -/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity, IkarusErrorData * error_out); - -/// \see ikarus_entity_to_object -IKA_API struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity, IkarusErrorData * error_out); - IKARUS_END_HEADER // @} diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index 7dcc97f..6a46e17 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -19,62 +19,6 @@ IKARUS_BEGIN_HEADER /// \brief A generic object. Wraps all types of objects, including folders. struct IkarusObject; -/// \brief Fetches the project of an object. -/// \param object The object to fetch the project from. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The project of the object or null if an error occurs. -IKA_API struct IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErrorData * error_out); - -/// \brief Fetches the name of an object. -/// \param object The object to fetch the name from. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The name of the object or null if an error occurs. -IKA_API char const * ikarus_object_get_name(IkarusObject const * object, IkarusErrorData * error_out); - -/// \brief Sets the name of an object. -/// \param object The object to set the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_name The new name of the object. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param error_out \see errors.h -IKA_API void ikarus_object_set_name(IkarusObject const * object, char const * new_name, IkarusErrorData * error_out); - -/// \brief Fetches the information of an object. -/// \param object The object to fetch the information from. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The information of the object or null if an error occurs. -IKA_API char const * ikarus_object_get_info(IkarusObject const * object, IkarusErrorData * error_out); - -/// \brief Sets the information of an object. -/// \param object The object to set the information of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_info The new information of the object. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -IKA_API void ikarus_object_set_info(IkarusObject const * object, char const * new_info, IkarusErrorData * error_out); - -/// \brief Compares two objects for equality. -/// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the -/// same object. -/// \param lhs The left hand side object. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param rhs The right hand side object. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return True if the objects are equal, false otherwise. -IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out); - /// \brief Visits an object. Calling the appropriate function for the object's type. /// \param object The object to visit. /// \param blueprint_visitor The function to call if the object is a blueprint. Skipped if null. @@ -84,9 +28,9 @@ IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const /// \param error_out \see errors.h IKA_API void ikarus_object_visit( IkarusObject * object, - void (*blueprint_visitor)(struct IkarusBlueprint *, void *), - void (*property_visitor)(struct IkarusProperty *, void *), - void (*entity_visitor)(struct IkarusEntity *, void *), + void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *), + void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *), + void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *), void * data, IkarusErrorData * error_out ); @@ -94,9 +38,9 @@ IKA_API void ikarus_object_visit( /// \see ikarus_object_visit IKA_API void ikarus_object_visit_const( IkarusObject const * object, - void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), - void (*property_visitor)(struct IkarusProperty const *, void *), - void (*entity_visitor)(struct IkarusEntity const *, void *), + void (*blueprint_visitor)(struct IkarusBlueprint const *, IkarusErrorData * error_out, void *), + void (*property_visitor)(struct IkarusProperty const *, IkarusErrorData * error_out, void *), + void (*entity_visitor)(struct IkarusEntity const *, IkarusErrorData * error_out, void *), void * data, IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index 7a94079..589854f 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -70,7 +70,6 @@ IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The type info of the property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Gets the source of a property. @@ -80,7 +79,7 @@ IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * prope /// \param error_out \see errors.h /// \return The source of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property, IkarusErrorData * error_out); +IKA_API struct IkarusPropertyScope const * ikarus_property_get_scope(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Gets the default value of a property. /// \param property The property to get the type info of. diff --git a/include/ikarus/objects/properties/property_source.h b/include/ikarus/objects/properties/property_scope.h similarity index 86% rename from include/ikarus/objects/properties/property_source.h rename to include/ikarus/objects/properties/property_scope.h index f1da29d..a211e67 100644 --- a/include/ikarus/objects/properties/property_source.h +++ b/include/ikarus/objects/properties/property_scope.h @@ -11,7 +11,7 @@ IKARUS_BEGIN_HEADER -struct IkarusPropertySource; +struct IkarusPropertyScope; /// \brief Creates an blueprint property source. /// \param blueprint The blueprint to create the property source for. @@ -20,7 +20,7 @@ struct IkarusPropertySource; /// \param error_out \see errors.h /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * +IKA_API struct IkarusPropertyScope * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \brief Creates an entity property source. @@ -30,7 +30,7 @@ ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint, Ikar /// \param error_out \see errors.h /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity, IkarusErrorData * error_out); +IKA_API struct IkarusPropertyScope * ikarus_property_source_create_entity(struct IkarusEntity * entity, IkarusErrorData * error_out); /// \brief Visits a property source, calling the appropriate callback. /// \param property_source The property source to visit. @@ -41,7 +41,7 @@ IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struc /// \param user_data User data to pass to the callbacks. /// \param error_out \see errors.h IKA_API void ikarus_property_source_visit( - struct IkarusPropertySource * property_source, + struct IkarusPropertyScope * property_source, void (*blueprint_visitor)(struct IkarusBlueprint *, void *), void (*entity_visitor)(struct IkarusEntity *, void *), void * user_data, @@ -50,7 +50,7 @@ IKA_API void ikarus_property_source_visit( /// \see ikarus_property_source_visit IKA_API void ikarus_property_source_visit_const( - struct IkarusPropertySource const * property_source, + struct IkarusPropertyScope const * property_source, void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), void (*entity_visitor)(struct IkarusEntity const *, void *), void * user_data, diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 117909e..f1f083a 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -80,29 +80,6 @@ IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_n /// \remark Ownership remains with libikarus, must not be freed. IKA_API char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out); -/// \brief Gets the blueprints of a project. -/// \param project The project to get the blueprints of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param blueprints_out The buffer to write the blueprints to. -/// \pre \li Must not be null. -/// \param blueprints_out_size The size of the buffer. -/// \param error_out \see errors.h -IKA_API void ikarus_project_get_blueprints( - IkarusProject * project, - struct IkarusBlueprint ** blueprints_out, - size_t blueprints_out_size, - IkarusErrorData * error_out -); - -/// \brief Gets the number of blueprints of a project. -/// \param project The project to get the number of blueprints of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); - /// \brief Gets the entities of a project. /// \param project The project to get the entities of. /// \pre \li Must not be null. @@ -126,6 +103,71 @@ IKA_API void ikarus_project_get_entities( /// \return The number of entities or undefined if an error occurs. IKA_API size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusErrorData * error_out); +/// \brief Gets the blueprints of a project. +/// \param project The project to get the blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprints_out The buffer to write the blueprints to. +/// \pre \li Must not be null. +/// \param blueprints_out_size The size of the buffer. +/// \param error_out \see errors.h +IKA_API void ikarus_project_get_blueprints( + IkarusProject * project, + struct IkarusBlueprint ** blueprints_out, + size_t blueprints_out_size, + IkarusErrorData * error_out +); + +/// \brief Gets the number of blueprints of a project. +/// \param project The project to get the number of blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The number of blueprints or undefined if an error occurs. +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); + +/// \brief Finds an entity by a given name. +/// \param project The project to search. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name to search for. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param error_out \see errors.h +/// \return The entity with the given name or null if none was found. +IKA_API struct IkarusEntity* get_entity_by_name( + IkarusProject const * project, + char const * name, + IkarusErrorData * error_out +); + +/// \brief Finds a property by a given name. +/// \param project The project to search. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param scope The scope of the property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark Property names are unique only within their scope. +/// \param name The name to search for. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param error_out \see errors.h +/// \return The property with the given name or null if none was found. +IKA_API struct IkarusProperty * +get_property_by_name(IkarusProject const * project, struct IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out); + +/// \brief Finds a blueprint by a given name. +/// \param project The project to search. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name to search for. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param error_out \see errors.h +/// \return The blueprint with the given name or null if none was found. +IKA_API struct IkarusBlueprint * get_blueprints_by_name(IkarusProject const * project, char const * name, IkarusErrorData * error_out); + IKARUS_END_HEADER /// @} diff --git a/src/errors.cpp b/src/ikarus/errors.cpp similarity index 98% rename from src/errors.cpp rename to src/ikarus/errors.cpp index 4441fe9..d98b28c 100644 --- a/src/errors.cpp +++ b/src/ikarus/errors.cpp @@ -1,13 +1,13 @@ #include "ikarus/errors.h" -#include "cppbase/functional.hpp" - #include #include #include +#include + char const * get_error_info_name(IkarusErrorInfo info) { switch (info) { case IkarusErrorInfo_None: return "None"; diff --git a/src/errors.hpp b/src/ikarus/errors.hpp similarity index 100% rename from src/errors.hpp rename to src/ikarus/errors.hpp diff --git a/src/global.cpp b/src/ikarus/global.cpp similarity index 100% rename from src/global.cpp rename to src/ikarus/global.cpp diff --git a/src/id.cpp b/src/ikarus/id.cpp similarity index 93% rename from src/id.cpp rename to src/ikarus/id.cpp index 4269215..031c490 100644 --- a/src/id.cpp +++ b/src/ikarus/id.cpp @@ -3,7 +3,7 @@ #include constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; -constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; +constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; auto ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); diff --git a/src/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp similarity index 83% rename from src/objects/blueprint.cpp rename to src/ikarus/objects/blueprint.cpp index e5f3f0c..326129d 100644 --- a/src/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -1,24 +1,23 @@ #include "ikarus/objects/blueprint.h" -#include "ikarus/objects/properties/property.h" -#include "objects/blueprint.hpp" - #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusObject{project, id} {} IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - 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_FAIL_IF_NAME_INVALID(name, project, nullptr, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, @@ -50,6 +49,18 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * erro blueprint->project->uncache(blueprint); } +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, @@ -147,17 +158,3 @@ size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprin return ret; } - -// No existence checks here for performance reasons. All methods on IkarusObject perform this check anyway. - -IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(blueprint, nullptr); - - return static_cast(blueprint); -} - -IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(blueprint, nullptr); - - return static_cast(blueprint); -} diff --git a/src/objects/blueprint.hpp b/src/ikarus/objects/blueprint.hpp similarity index 71% rename from src/objects/blueprint.hpp rename to src/ikarus/objects/blueprint.hpp index 61aca64..30c57b0 100644 --- a/src/objects/blueprint.hpp +++ b/src/ikarus/objects/blueprint.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include struct IkarusBlueprint : IkarusObject { +public: IkarusBlueprint(struct IkarusProject * project, IkarusId id); IkarusBlueprint(IkarusBlueprint const &) = default; @@ -12,4 +13,9 @@ struct IkarusBlueprint : IkarusObject { IkarusBlueprint & operator=(IkarusBlueprint &&) = default; ~IkarusBlueprint() override = default; + +public: + inline std::string_view get_table_name() const noexcept override { + return "blueprints"; + } }; diff --git a/src/objects/entity.cpp b/src/ikarus/objects/entity.cpp similarity index 88% rename from src/objects/entity.cpp rename to src/ikarus/objects/entity.cpp index 6c1839a..94e8b75 100644 --- a/src/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -1,19 +1,18 @@ #include "entity.hpp" -#include "values/value.hpp" - #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - 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_FAIL_IF_NAME_INVALID(name, project, nullptr, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, @@ -21,9 +20,9 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * "failed to create entity: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Entity, name, "")); + TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?, ?, ?)", IkarusObjectType_Entity)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Entity); - TRY(db->execute("INSERT INTO `entities`(`id`) VALUES(?)", id)); + TRY(db->execute("INSERT INTO `entities`(`id`, `name`) VALUES(?)", id, name)); return cppbase::ok(id); }) ); @@ -45,6 +44,18 @@ void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { entity->project->uncache(entity); } +IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity, IkarusErrorData * error_out) { + return ikarus::util::object_get_project(entity, error_out); +} + +char const * ikarus_entity_get_name(IkarusEntity const * entity, IkarusErrorData * error_out) { + return ikarus::util::object_get_name(entity, error_out); +} + +void ikarus_entity_set_name(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) { + ikarus::util::object_set_name(entity, name, error_out); +} + bool ikarus_entity_is_linked_to_blueprint( IkarusEntity const * entity, struct IkarusBlueprint const * blueprint, @@ -273,17 +284,3 @@ void ikarus_entity_set_value( ) ); } - -// No existence checks here for performance reasons. All methods on IkarusObject perform this check anyway. - -struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - - return static_cast(entity); -} - -struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - - return static_cast(entity); -} diff --git a/src/objects/entity.hpp b/src/ikarus/objects/entity.hpp similarity index 72% rename from src/objects/entity.hpp rename to src/ikarus/objects/entity.hpp index 92cf4c6..586c88f 100644 --- a/src/objects/entity.hpp +++ b/src/ikarus/objects/entity.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include struct IkarusEntity : IkarusObject { +public: inline IkarusEntity(struct IkarusProject * project, IkarusId id): IkarusObject{project, id} {} @@ -13,4 +14,9 @@ struct IkarusEntity : IkarusObject { IkarusEntity & operator=(IkarusEntity &&) = default; ~IkarusEntity() override = default; + +public: + inline std::string_view get_table_name() const noexcept override { + return "entities"; + } }; diff --git a/src/ikarus/objects/object.cpp b/src/ikarus/objects/object.cpp new file mode 100644 index 0000000..d233783 --- /dev/null +++ b/src/ikarus/objects/object.cpp @@ -0,0 +1,93 @@ +#include "object.hpp" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): + project{project}, + id{id} {} + +void ikarus_object_visit( + IkarusObject * object, + void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *), + void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *), + void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *), + void * data, + IkarusErrorData * error_out +) { + struct Data { + void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *); + void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *); + void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *); + void * data; + }; + + Data passthru_data{blueprint_visitor, property_visitor, entity_visitor, data}; + + ikarus_object_visit_const( + object, + [](IkarusBlueprint const * blueprint, IkarusErrorData * error_out, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->blueprint_visitor(const_cast(blueprint), error_out, passthru_data->data); + }, + [](IkarusProperty const * property, IkarusErrorData * error_out, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->property_visitor(const_cast(property), error_out, passthru_data->data); + }, + [](IkarusEntity const * entity, IkarusErrorData * error_out, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->entity_visitor(const_cast(entity), error_out, passthru_data->data); + }, + &passthru_data, + error_out + ); +} + +void ikarus_object_visit_const( + IkarusObject const * object, + void (*blueprint_visitor)(struct IkarusBlueprint const *, IkarusErrorData * error_out, void *), + void (*property_visitor)(struct IkarusProperty const *, IkarusErrorData * error_out, void *), + void (*entity_visitor)(struct IkarusEntity const *, IkarusErrorData * error_out, void *), + void * data, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(object, ); + IKARUS_FAIL_IF_OBJECT_MISSING(object, ); + + switch (ikarus_id_get_object_type(object->id)) { + case IkarusObjectType_Entity: { + auto const * entity = dynamic_cast(object); + IKARUS_FAIL_IF(entity == nullptr, , "object with entity id wasn't a entity", IkarusErrorInfo_LibIkarus_InvalidState); + entity_visitor(entity, error_out, data); + } + case IkarusObjectType_Blueprint: { + auto const * blueprint = dynamic_cast(object); + IKARUS_FAIL_IF(blueprint == nullptr, , "object with blueprint id wasn't a blueprint", IkarusErrorInfo_LibIkarus_InvalidState); + blueprint_visitor(blueprint, error_out, data); + } + case IkarusObjectType_Property: { + auto const * property = dynamic_cast(object); + IKARUS_FAIL_IF(property == nullptr, , "object with property id wasn't a property", IkarusErrorInfo_LibIkarus_InvalidState); + property_visitor(property, error_out, data); + } + default: { + IKARUS_FAIL( + , + fmt::format("unknown object type: {}", ikarus_object_type_to_string(ikarus_id_get_object_type(object->id))), + IkarusErrorInfo_LibIkarus_InvalidState + ); + } + } + + // not needed, but a good delineation of our intention if this function gets extended + IKARUS_FAIL_IF_ERROR() +} diff --git a/src/objects/object.hpp b/src/ikarus/objects/object.hpp similarity index 82% rename from src/objects/object.hpp rename to src/ikarus/objects/object.hpp index 56e141b..20eed59 100644 --- a/src/objects/object.hpp +++ b/src/ikarus/objects/object.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include struct IkarusObject { @@ -14,6 +16,9 @@ public: virtual ~IkarusObject() = default; +public: + virtual std::string_view get_table_name() const noexcept = 0; + public: struct IkarusProject * project; IkarusId id; diff --git a/src/objects/object_type.cpp b/src/ikarus/objects/object_type.cpp similarity index 100% rename from src/objects/object_type.cpp rename to src/ikarus/objects/object_type.cpp diff --git a/src/objects/properties/number_property.cpp b/src/ikarus/objects/properties/number_property.cpp similarity index 70% rename from src/objects/properties/number_property.cpp rename to src/ikarus/objects/properties/number_property.cpp index b94429d..43a966c 100644 --- a/src/objects/properties/number_property.cpp +++ b/src/ikarus/objects/properties/number_property.cpp @@ -1,13 +1,11 @@ -#include "ikarus/objects/properties/number_property.h" +#include "number_property.hpp" #include #include - -#include -#include -#include -#include +#include +#include +#include IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} @@ -15,11 +13,10 @@ IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, IkarusId id) IkarusNumberProperty * ikarus_number_property_create( struct IkarusProject * project, char const * name, - struct IkarusPropertySource * property_source, - struct IkarusNumberValue * default_value, + struct IkarusPropertyScope * property_source, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, default_value, error_out); + return ikarus::util::create_property(project, name, property_source, error_out); } IkarusNumberValue * ikarus_number_property_get_default_value(IkarusNumberProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/number_property.hpp b/src/ikarus/objects/properties/number_property.hpp similarity index 77% rename from src/objects/properties/number_property.hpp rename to src/ikarus/objects/properties/number_property.hpp index 22ca3d3..eddb189 100644 --- a/src/objects/properties/number_property.hpp +++ b/src/ikarus/objects/properties/number_property.hpp @@ -1,9 +1,8 @@ #pragma once +#include #include - -#include -#include +#include struct IkarusNumberProperty : IkarusProperty { public: diff --git a/src/objects/properties/property.cpp b/src/ikarus/objects/properties/property.cpp similarity index 81% rename from src/objects/properties/property.cpp rename to src/ikarus/objects/properties/property.cpp index d3c27be..f45adbb 100644 --- a/src/objects/properties/property.cpp +++ b/src/ikarus/objects/properties/property.cpp @@ -2,14 +2,13 @@ #include +#include #include +#include #include - -#include -#include -#include -#include -#include +#include +#include +#include IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): IkarusObject{project, id}, @@ -29,6 +28,18 @@ IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * property->project->uncache(property); } +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); +} + IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(property, IkarusPropertyType_Toggle); IKARUS_FAIL_IF_OBJECT_MISSING(property, IkarusPropertyType_Toggle); @@ -44,7 +55,7 @@ IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, Ika return static_cast(ret); } -IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property, IkarusErrorData * error_out) { +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); @@ -57,8 +68,8 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p ); switch (ikarus_id_get_object_type(source)) { - case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->project->get_blueprint(source)}; - case IkarusObjectType_Entity: return new IkarusPropertySource{property->project->get_entity(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, diff --git a/src/objects/properties/property.hpp b/src/ikarus/objects/properties/property.hpp similarity index 79% rename from src/objects/properties/property.hpp rename to src/ikarus/objects/properties/property.hpp index 3a95d6b..5c37d7c 100644 --- a/src/objects/properties/property.hpp +++ b/src/ikarus/objects/properties/property.hpp @@ -2,7 +2,7 @@ #include -#include +#include struct IkarusProperty : IkarusObject { public: @@ -19,6 +19,11 @@ public: ~IkarusProperty() override = default; +public: + inline std::string_view get_table_name() const noexcept override { + return "properties"; + } + public: Data data; }; diff --git a/src/objects/properties/property_source.cpp b/src/ikarus/objects/properties/property_scope.cpp similarity index 58% rename from src/objects/properties/property_source.cpp rename to src/ikarus/objects/properties/property_scope.cpp index 3e31bb3..a0b9ade 100644 --- a/src/objects/properties/property_source.cpp +++ b/src/ikarus/objects/properties/property_scope.cpp @@ -1,17 +1,16 @@ -#include "property_source.hpp" +#include "property_scope.hpp" -#include - -#include -#include #include -IkarusPropertySource::IkarusPropertySource(Data data): +#include +#include + +IkarusPropertyScope::IkarusPropertyScope(Data data): data{data} {} -IkarusId IkarusPropertySource::get_id() const { - return boost::variant2::visit( - cppbase::overloaded { +IkarusId IkarusPropertyScope::get_id() const { + return std::visit( + cppbase::overloaded{ [](IkarusBlueprint const * blueprint) { return blueprint->id; }, [](IkarusEntity const * entity) { return entity->id; } }, @@ -19,37 +18,37 @@ IkarusId IkarusPropertySource::get_id() const { ); } -IkarusPropertySource * ikarus_property_source_create_blueprint(IkarusBlueprint * blueprint) { - return new IkarusPropertySource{blueprint}; +IkarusPropertyScope * ikarus_property_source_create_blueprint(IkarusBlueprint * blueprint) { + return new IkarusPropertyScope{blueprint}; } -IkarusPropertySource * ikarus_property_source_create_entity(IkarusEntity * entity) { - return new IkarusPropertySource{entity}; +IkarusPropertyScope * ikarus_property_source_create_entity(IkarusEntity * entity) { + return new IkarusPropertyScope{entity}; } void ikarus_property_source_visit( - struct IkarusPropertySource * property_source, + struct IkarusPropertyScope * property_source, void (*blueprint_visitor)(struct IkarusBlueprint *, void *), void (*entity_visitor)(struct IkarusEntity *, void *), void * user_data ) { - boost::variant2::visit( - boost::make_overloaded_function( + std::visit( + cppbase::overloaded{ [blueprint_visitor, user_data](IkarusBlueprint * blueprint) { blueprint_visitor(blueprint, user_data); }, [entity_visitor, user_data](IkarusEntity * entity) { entity_visitor(entity, user_data); } - ), + }, property_source->data ); } void ikarus_property_source_visit_const( - struct IkarusPropertySource const * property_source, + struct IkarusPropertyScope const * property_source, void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), void (*entity_visitor)(struct IkarusEntity const *, void *), void * user_data ) { - boost::variant2::visit( - boost::make_overloaded_function( + std::visit( + cppbase::overloaded( [blueprint_visitor, user_data](IkarusBlueprint const * blueprint) { blueprint_visitor(blueprint, user_data); }, [entity_visitor, user_data](IkarusEntity const * entity) { entity_visitor(entity, user_data); } ), diff --git a/src/ikarus/objects/properties/property_scope.hpp b/src/ikarus/objects/properties/property_scope.hpp new file mode 100644 index 0000000..ab8832a --- /dev/null +++ b/src/ikarus/objects/properties/property_scope.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include +#include + +#define IKARUS_FAIL_IF_PROPERTY_SCOPE_INVALID(scope, ret) \ + std::visit( \ + cppbase::overloaded{[error_out](auto const * object) { \ + IKARUS_FAIL_IF_NULL(object, ); \ + IKARUS_FAIL_IF_OBJECT_MISSING(object, ); \ + }}, \ + scope->data \ + ); \ + \ + IKARUS_FAIL_IF_ERROR(nullptr); + +struct IkarusPropertyScope { +public: + using Data = std::variant; + +public: + explicit IkarusPropertyScope(Data data); + + IkarusPropertyScope(IkarusPropertyScope const &) = default; + IkarusPropertyScope(IkarusPropertyScope &&) = default; + + IkarusPropertyScope & operator=(IkarusPropertyScope const &) = default; + IkarusPropertyScope & operator=(IkarusPropertyScope &&) = default; + + virtual ~IkarusPropertyScope() = default; + +public: + [[nodiscard]] IkarusId get_id() const; + +public: + Data data; +}; diff --git a/src/objects/properties/text_property.cpp b/src/ikarus/objects/properties/text_property.cpp similarity index 77% rename from src/objects/properties/text_property.cpp rename to src/ikarus/objects/properties/text_property.cpp index c7541ff..6aaf483 100644 --- a/src/objects/properties/text_property.cpp +++ b/src/ikarus/objects/properties/text_property.cpp @@ -3,10 +3,9 @@ #include #include - -#include -#include -#include +#include +#include +#include IkarusTextProperty::IkarusTextProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} @@ -14,11 +13,10 @@ IkarusTextProperty::IkarusTextProperty(IkarusProject * project, IkarusId id): IkarusTextProperty * ikarus_text_property_create( struct IkarusProject * project, char const * name, - struct IkarusPropertySource * property_source, - struct IkarusTextValue * default_value, + struct IkarusPropertyScope * property_source, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, default_value, error_out); + return ikarus::util::create_property(project, name, property_source, error_out); } IkarusTextValue * ikarus_text_property_get_default_value(IkarusTextProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/text_property.hpp b/src/ikarus/objects/properties/text_property.hpp similarity index 77% rename from src/objects/properties/text_property.hpp rename to src/ikarus/objects/properties/text_property.hpp index a3b7e97..aca2cfa 100644 --- a/src/objects/properties/text_property.hpp +++ b/src/ikarus/objects/properties/text_property.hpp @@ -1,9 +1,8 @@ #pragma once +#include #include - -#include -#include +#include struct IkarusTextProperty : IkarusProperty { public: diff --git a/src/objects/properties/toggle_property.cpp b/src/ikarus/objects/properties/toggle_property.cpp similarity index 77% rename from src/objects/properties/toggle_property.cpp rename to src/ikarus/objects/properties/toggle_property.cpp index 2cde654..2449b39 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/ikarus/objects/properties/toggle_property.cpp @@ -3,10 +3,9 @@ #include #include - -#include -#include -#include +#include +#include +#include IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} @@ -14,11 +13,10 @@ IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id) IkarusToggleProperty * ikarus_toggle_property_create( struct IkarusProject * project, char const * name, - struct IkarusPropertySource * property_source, - struct IkarusToggleValue * default_value, + struct IkarusPropertyScope * property_source, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, default_value, error_out); + return ikarus::util::create_property(project, name, property_source, error_out); } IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/toggle_property.hpp b/src/ikarus/objects/properties/toggle_property.hpp similarity index 77% rename from src/objects/properties/toggle_property.hpp rename to src/ikarus/objects/properties/toggle_property.hpp index 247cbcb..bf53c19 100644 --- a/src/objects/properties/toggle_property.hpp +++ b/src/ikarus/objects/properties/toggle_property.hpp @@ -1,9 +1,8 @@ #pragma once +#include #include - -#include -#include +#include struct IkarusToggleProperty : IkarusProperty { public: diff --git a/src/objects/properties/util.hpp b/src/ikarus/objects/properties/util.hpp similarity index 73% rename from src/objects/properties/util.hpp rename to src/ikarus/objects/properties/util.hpp index d9321c7..092296e 100644 --- a/src/objects/properties/util.hpp +++ b/src/ikarus/objects/properties/util.hpp @@ -4,48 +4,40 @@ #include +#include #include - -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include namespace ikarus::util { template T * create_property( struct IkarusProject * project, char const * name, - struct IkarusPropertySource * property_source, - typename T::value_type * default_value, + struct IkarusPropertyScope * property_scope, IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NULL(name, nullptr); - IKARUS_FAIL_IF_NULL(property_source, nullptr); - IKARUS_FAIL_IF( - cppbase::is_empty_or_blank(name), - nullptr, - fmt::format("{} name cannot be empty or blank", boost::typeindex::type_id().pretty_name()), - IkarusErrorInfo_Client_InvalidInput - ) - IKARUS_FAIL_IF_NULL(default_value, nullptr); + IKARUS_FAIL_IF_NULL(property_scope, nullptr); + IKARUS_FAIL_IF_PROPERTY_SCOPE_INVALID(property_scope, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, project, property_scope, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, nullptr, "failed to create property: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->transact([name, property_source, default_value](auto * db) -> cppbase::Result { + project->db->transact([name, property_scope](auto * db) -> cppbase::Result { TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Property, name, "")); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Property); TRY(db->execute( - "INSERT INTO `properties`(`id`, `type`, `source`, `default_value`) VALUES(?, ?, ?, ?)", + "INSERT INTO `properties`(`id`, `type`, `source`) VALUES(?, ?, ?)", id, T::PropertyType, - property_source->get_id(), - boost::json::serialize(default_value->to_json()) + property_scope->get_id() )); return cppbase::ok(id); }) diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp new file mode 100644 index 0000000..29857cf --- /dev/null +++ b/src/ikarus/objects/util.hpp @@ -0,0 +1,125 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ikarus::util { + +template +[[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 +[[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(fmt::format("SELECT `name` FROM `{}` WHERE `id` = ?", object->get_table_name()), object->id) + ); + + return strdup(ret.data()); +} + +[[nodiscard]] inline bool name_is_unique( + IkarusProject const * project, + std::string_view name, + [[maybe_unused]] IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +) { + IKARUS_VTRYRV_OR_FAIL( + bool const exists, + false, + "unable to check if blueprint name is unique", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT EXISTS(SELECT 1 FROM `blueprints` WHERE `name` = ?)", name) + ); + + return exists; +} + +[[nodiscard]] inline bool name_is_unique( + IkarusProject const * project, + std::string_view name, + [[maybe_unused]] IkarusEntity const * entity, + IkarusErrorData * error_out +) { + IKARUS_VTRYRV_OR_FAIL( + bool const exists, + false, + "unable to check if entity name is unique", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT EXISTS(SELECT 1 FROM `entities` WHERE `name` = ?)", name) + ); + + return exists; +} + +[[nodiscard]] inline bool +name_is_unique(IkarusProject const * project, std::string_view name, IkarusPropertyScope const * scope, IkarusErrorData * error_out) { + IKARUS_VTRYRV_OR_FAIL( + bool const exists, + false, + "unable to check if property name is unique", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT EXISTS(SELECT 1 FROM `properties` WHERE `name` = ? AND `scope` = ?)", name, scope->get_id()) + ); + + return exists; +} + +[[nodiscard]] inline bool +name_is_unique(IkarusProject * project, std::string_view name, IkarusProperty const * property, IkarusErrorData * error_out) { + std::unique_ptr scope{ikarus_property_get_scope(property, error_out)}; + IKARUS_FAIL_IF_ERROR(false); + + return name_is_unique(project, name, scope.get(), error_out); +} + +#define IKARUS_FAIL_IF_NAME_INVALID(name, project, object, ret, ...) \ + IKARUS_FAIL_IF_NULL(name, ret); \ + IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); \ + IKARUS_FAIL_IF( \ + !ikarus::util::name_is_unique(project, name, object, error_out), \ + ret, \ + "name must be unique", \ + IkarusErrorInfo_Client_InvalidInput \ + ); \ + IKARUS_FAIL_IF_ERROR(ret); + +template +void object_set_name(O * object, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(object, ); + IKARUS_FAIL_IF_OBJECT_MISSING(object, ); + IKARUS_FAIL_IF_NULL(name, ); + IKARUS_FAIL_IF_NAME_INVALID(name, object->project, object, ); + + IKARUS_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 diff --git a/src/persistence/migrations.hpp b/src/ikarus/persistence/migrations.hpp similarity index 90% rename from src/persistence/migrations.hpp rename to src/ikarus/persistence/migrations.hpp index c777e84..a46d1ee 100644 --- a/src/persistence/migrations.hpp +++ b/src/ikarus/persistence/migrations.hpp @@ -8,7 +8,7 @@ #include namespace ikarus { -CPPBASE_ASSET(m0_initial_layout, "persistence/migrations/m0_initial_layout.sql"); +CPPBASE_ASSET(m0_initial_layout, "ikarus/persistence/migrations/m0_initial_layout.sql"); class Migration : public sqlitecpp::Migration { public: diff --git a/src/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql similarity index 61% rename from src/persistence/migrations/m0_initial_layout.sql rename to src/ikarus/persistence/migrations/m0_initial_layout.sql index 99ba61b..0031cee 100644 --- a/src/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -2,36 +2,29 @@ CREATE TABLE `objects` ( `do_not_access_rowid_alias` INTEGER PRIMARY KEY, `type` INT NOT NULL, - `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`type` << 56)) VIRTUAL, - `name` TEXT NOT NULL, - `information` TEXT NOT NULL + `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`type` << 56)) VIRTUAL UNIQUE ) STRICT; CREATE UNIQUE INDEX `object_id` ON `objects` (`id`); CREATE INDEX `object_type` ON `objects` (`type`); -CREATE VIRTUAL TABLE `objects_fts` USING fts5 -( - `name`, - `information`, - content='objects', - content_rowid='id', - tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" -); - CREATE TABLE `entities` ( - `id` INT, + `id` INT, + `name` TEXT NOT NULL, PRIMARY KEY (`id`), + UNIQUE (`name`), FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; CREATE TABLE `blueprints` ( - `id` INT, + `id` INT, + `name` TEXT NOT NULL, PRIMARY KEY (`id`), + UNIQUE (`name`), FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; @@ -40,23 +33,22 @@ CREATE TABLE `entity_blueprint_links` `entity` INT NOT NULL, `blueprint` INT NOT NULL, - PRIMARY KEY (`entity`), - UNIQUE (`entity`, `blueprint`), + PRIMARY KEY (`entity`, `blueprint`), FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, FOREIGN KEY (`blueprint`) REFERENCES `blueprints` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; -CREATE INDEX `entity_blueprints_blueprint` ON `entity_blueprint_links` (`blueprint`); - CREATE TABLE `properties` ( `id` INT, + `name` TEXT NOT NULL, `type` INT NOT NULL, `default_value` TEXT NOT NULL, `settings` TEXT NOT NULL, `source` INT NOT NULL, PRIMARY KEY (`id`), + UNIQUE(`source`, `name`), FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE, FOREIGN KEY (`source`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; @@ -64,24 +56,6 @@ CREATE TABLE `properties` CREATE INDEX `properties_type` ON `properties` (`type`); CREATE INDEX `properties_source` ON `properties` (`source`); -CREATE -VIRTUAL TABLE `property_default_value_fts` USING fts5 -( - `default_value`, - content='properties', - content_rowid='object_id', - tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" -); - -CREATE -VIRTUAL TABLE `property_settings_fts` USING fts5 -( - `settings`, - content='properties', - content_rowid='object_id', - tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" -); - CREATE TABLE `values` ( `entity` INT NOT NULL, @@ -92,11 +66,3 @@ CREATE TABLE `values` FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, FOREIGN KEY (`property`) REFERENCES `properties` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; - -CREATE -VIRTUAL TABLE `values_fts` USING fts5 -( - `value`, - content='values', - tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" -); diff --git a/src/persistence/project.cpp b/src/ikarus/persistence/project.cpp similarity index 95% rename from src/persistence/project.cpp rename to src/ikarus/persistence/project.cpp index 41ddbf7..6c460f7 100644 --- a/src/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -4,14 +4,14 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include IkarusProject::IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db): name{name}, diff --git a/src/persistence/project.hpp b/src/ikarus/persistence/project.hpp similarity index 100% rename from src/persistence/project.hpp rename to src/ikarus/persistence/project.hpp diff --git a/src/values/entity_property_value.cpp b/src/ikarus/values/entity_property_value.cpp similarity index 90% rename from src/values/entity_property_value.cpp rename to src/ikarus/values/entity_property_value.cpp index d1580ea..165383d 100644 --- a/src/values/entity_property_value.cpp +++ b/src/ikarus/values/entity_property_value.cpp @@ -1,6 +1,6 @@ -#include "values/entity_property_value.hpp" +#include "entity_property_value.hpp" -#include +#include IkarusEntity const * ikarus_entity_property_value_get_entity(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(value, nullptr); diff --git a/src/values/entity_property_value.hpp b/src/ikarus/values/entity_property_value.hpp similarity index 100% rename from src/values/entity_property_value.hpp rename to src/ikarus/values/entity_property_value.hpp diff --git a/src/values/number_value.cpp b/src/ikarus/values/number_value.cpp similarity index 96% rename from src/values/number_value.cpp rename to src/ikarus/values/number_value.cpp index c339149..37eab49 100644 --- a/src/values/number_value.cpp +++ b/src/ikarus/values/number_value.cpp @@ -2,8 +2,8 @@ #include -#include -#include +#include +#include IkarusNumberValue::IkarusNumberValue(): IkarusValue{this} {} diff --git a/src/values/number_value.hpp b/src/ikarus/values/number_value.hpp similarity index 94% rename from src/values/number_value.hpp rename to src/ikarus/values/number_value.hpp index 8644d3e..6c6cb70 100644 --- a/src/values/number_value.hpp +++ b/src/ikarus/values/number_value.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include struct IkarusNumberValue : IkarusValue { public: diff --git a/src/values/text_value.cpp b/src/ikarus/values/text_value.cpp similarity index 96% rename from src/values/text_value.cpp rename to src/ikarus/values/text_value.cpp index 64006ac..8235c6d 100644 --- a/src/values/text_value.cpp +++ b/src/ikarus/values/text_value.cpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include IkarusTextValue::IkarusTextValue(): IkarusValue{this} {} diff --git a/src/values/text_value.hpp b/src/ikarus/values/text_value.hpp similarity index 94% rename from src/values/text_value.hpp rename to src/ikarus/values/text_value.hpp index 40d3945..512bb8d 100644 --- a/src/values/text_value.hpp +++ b/src/ikarus/values/text_value.hpp @@ -2,7 +2,7 @@ #include -#include +#include struct IkarusTextValue : IkarusValue { public: diff --git a/src/values/toggle_value.cpp b/src/ikarus/values/toggle_value.cpp similarity index 96% rename from src/values/toggle_value.cpp rename to src/ikarus/values/toggle_value.cpp index dabbe00..7bec1f2 100644 --- a/src/values/toggle_value.cpp +++ b/src/ikarus/values/toggle_value.cpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include IkarusToggleValue::IkarusToggleValue(): IkarusValue{this} {} diff --git a/src/values/toggle_value.hpp b/src/ikarus/values/toggle_value.hpp similarity index 94% rename from src/values/toggle_value.hpp rename to src/ikarus/values/toggle_value.hpp index 20a5dd6..6882ecc 100644 --- a/src/values/toggle_value.hpp +++ b/src/ikarus/values/toggle_value.hpp @@ -2,7 +2,7 @@ #include -#include +#include struct IkarusToggleValue : IkarusValue { public: diff --git a/src/values/value.cpp b/src/ikarus/values/value.cpp similarity index 96% rename from src/values/value.cpp rename to src/ikarus/values/value.cpp index 812b2c8..bbea7f3 100644 --- a/src/values/value.cpp +++ b/src/ikarus/values/value.cpp @@ -10,11 +10,10 @@ #include #include - -#include -#include -#include -#include +#include +#include +#include +#include IkarusValue::IkarusValue(Data data): data(data) {} diff --git a/src/values/value.hpp b/src/ikarus/values/value.hpp similarity index 91% rename from src/values/value.hpp rename to src/ikarus/values/value.hpp index 94b423d..1e5791b 100644 --- a/src/values/value.hpp +++ b/src/ikarus/values/value.hpp @@ -5,8 +5,8 @@ #include -#include -#include +#include +#include struct IkarusValue { public: @@ -41,7 +41,7 @@ struct fmt::formatter { return ctx.end(); } - constexpr static auto format([[maybe_unused]]IkarusValue::FromJsonError const & error, fmt::format_context & ctx) { + constexpr static auto format([[maybe_unused]] IkarusValue::FromJsonError const & error, fmt::format_context & ctx) { return fmt::format_to(ctx.out(), "unable to parse ikarus value JSON"); } }; diff --git a/src/values/value_base.hpp b/src/ikarus/values/value_base.hpp similarity index 100% rename from src/values/value_base.hpp rename to src/ikarus/values/value_base.hpp diff --git a/src/objects/object.cpp b/src/objects/object.cpp deleted file mode 100644 index e8a3353..0000000 --- a/src/objects/object.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "object.hpp" - -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include - -IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): - project{project}, - id{id} {} - -IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(object, nullptr); - IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); - - return object->project; -} - -char const * ikarus_object_get_name(IkarusObject const * object, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(object, nullptr); - IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); - - IKARUS_VTRYRV_OR_FAIL( - std::string ret, - nullptr, - "unable to get object name: {}", - IkarusErrorInfo_Database_QueryFailed, - object->project->db->template query_one("SELECT `name` FROM `objects` WHERE `id` = ?", object->id) - ); - - return strdup(ret.data()); -} - -void ikarus_object_set_name(IkarusObject * 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(cppbase::is_empty_or_blank(name), , "name must not be empty", IkarusErrorInfo_Client_InvalidInput); - - IKARUS_TRYRV_OR_FAIL( - , - "unable to set object name: {}", - IkarusErrorInfo_Database_QueryFailed, - object->project->db->template execute("UPDATE `objects` SET `name` = ? WHERE `id` = ?", name, object->id) - ); -} - -char const * ikarus_object_get_information(IkarusObject const * object, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(object, nullptr); - IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); - - IKARUS_VTRYRV_OR_FAIL( - std::string ret, - nullptr, - "unable to get object information: {}", - IkarusErrorInfo_Database_QueryFailed, - object->project->db->template query_one("SELECT `information` FROM `objects` WHERE `id` = ?", object->id) - ); - - return strdup(ret.data()); -} - -void ikarus_object_set_information(IkarusObject * object, char const * information, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(object, ); - IKARUS_FAIL_IF_OBJECT_MISSING(object, ); - IKARUS_FAIL_IF_NULL(information, ); - IKARUS_FAIL_IF(cppbase::is_empty_or_blank(information), , "information must not be empty", IkarusErrorInfo_Client_InvalidInput); - - IKARUS_TRYRV_OR_FAIL( - , - "unable to set object information: {}", - IkarusErrorInfo_Database_QueryFailed, - object->project->db->template execute("UPDATE `objects` SET `information` = ? WHERE `id` = ?", information, object->id) - ); -} - -bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(lhs, false); - IKARUS_FAIL_IF_OBJECT_MISSING(lhs, false); - IKARUS_FAIL_IF_NULL(rhs, false); - IKARUS_FAIL_IF_OBJECT_MISSING(rhs, false); - - return lhs->id == rhs->id; -} - -void ikarus_object_visit( - IkarusObject * object, - void (*blueprint_visitor)(struct IkarusBlueprint *, void *), - void (*property_visitor)(struct IkarusProperty *, void *), - void (*entity_visitor)(struct IkarusEntity *, void *), - void * data, - IkarusErrorData * error_out -) { - struct Data { - void (*blueprint_visitor)(struct IkarusBlueprint *, void *); - void (*property_visitor)(struct IkarusProperty *, void *); - void (*entity_visitor)(struct IkarusEntity *, void *); - void * data; - }; - - Data passthru_data{ - blueprint_visitor, - property_visitor, - entity_visitor, - data, - }; - - ikarus_object_visit_const( - object, - [](IkarusBlueprint const * blueprint, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->blueprint_visitor(const_cast(blueprint), passthru_data->data); - }, - [](IkarusProperty const * property, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->property_visitor(const_cast(property), passthru_data->data); - }, - [](IkarusEntity const * entity, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->entity_visitor(const_cast(entity), passthru_data->data); - }, - &passthru_data, - error_out - ); -} - -void ikarus_object_visit_const( - IkarusObject const * object, - void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), - void (*property_visitor)(struct IkarusProperty const *, void *), - void (*entity_visitor)(struct IkarusEntity const *, void *), - void * data, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(object, ); - IKARUS_FAIL_IF_OBJECT_MISSING(object, ); - - switch (ikarus_id_get_object_type(object->id)) { - case IkarusObjectType_Entity: { - auto const * entity = dynamic_cast(object); - IKARUS_FAIL_IF(entity == nullptr, , "object with entity id wasn't a entity", IkarusErrorInfo_LibIkarus_InvalidState); - entity_visitor(entity, data); - } - case IkarusObjectType_Blueprint: { - auto const * blueprint = dynamic_cast(object); - IKARUS_FAIL_IF(blueprint == nullptr, , "object with blueprint id wasn't a blueprint", IkarusErrorInfo_LibIkarus_InvalidState); - blueprint_visitor(blueprint, data); - } - case IkarusObjectType_Property: { - auto const * property = dynamic_cast(object); - IKARUS_FAIL_IF(property == nullptr, , "object with property id wasn't a property", IkarusErrorInfo_LibIkarus_InvalidState); - property_visitor(property, data); - } - default: { - IKARUS_FAIL( - , - fmt::format("unknown object type: {}", ikarus_object_type_to_string(ikarus_id_get_object_type(object->id))), - IkarusErrorInfo_LibIkarus_InvalidState - ); - } - } -} diff --git a/src/objects/properties/property_source.hpp b/src/objects/properties/property_source.hpp deleted file mode 100644 index 672021d..0000000 --- a/src/objects/properties/property_source.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include -#include - -struct IkarusPropertySource { -public: - using Data = boost::variant2::variant; - -public: - explicit IkarusPropertySource(Data data); - - IkarusPropertySource(IkarusPropertySource const &) = default; - IkarusPropertySource(IkarusPropertySource &&) = default; - - IkarusPropertySource & operator=(IkarusPropertySource const &) = default; - IkarusPropertySource & operator=(IkarusPropertySource &&) = default; - - virtual ~IkarusPropertySource() = default; - -public: - [[nodiscard]] IkarusId get_id() const; - -public: - Data data; -}; diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 77e7260..eadf323 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 77e726036b52fff6f82ff2d44081f05aee7408a8 +Subproject commit eadf3237bfa853763b332777e9dc0f16df8cca71 From 9ff3d1720f6d70bcebda3e1c5e0bc4e386f68802 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 30 Jan 2024 09:33:59 +0100 Subject: [PATCH 046/166] fixup id generation Signed-off-by: Folling --- src/ikarus/id.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ikarus/id.cpp b/src/ikarus/id.cpp index 031c490..4269215 100644 --- a/src/ikarus/id.cpp +++ b/src/ikarus/id.cpp @@ -3,7 +3,7 @@ #include constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; -constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; +constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; auto ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); From 0ace4243cffda8fd1c1966391a3154fa2c14b4a6 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 30 Jan 2024 09:34:13 +0100 Subject: [PATCH 047/166] make project functions const where appropriate Signed-off-by: Folling --- include/ikarus/persistence/project.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index f1f083a..7847a78 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -89,7 +89,7 @@ IKA_API char const * ikarus_project_get_path(IkarusProject const * project, Ikar /// \param entities_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_entities( - IkarusProject * project, + IkarusProject const * project, struct IkarusEntity ** entities_out, size_t entities_out_size, IkarusErrorData * error_out @@ -101,7 +101,7 @@ IKA_API void ikarus_project_get_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of entities or undefined if an error occurs. -IKA_API size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Gets the blueprints of a project. /// \param project The project to get the blueprints of. @@ -112,7 +112,7 @@ IKA_API size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusEr /// \param blueprints_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_blueprints( - IkarusProject * project, + IkarusProject const * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size, IkarusErrorData * error_out @@ -135,11 +135,7 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The entity with the given name or null if none was found. -IKA_API struct IkarusEntity* get_entity_by_name( - IkarusProject const * project, - char const * name, - IkarusErrorData * error_out -); +IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject const * project, char const * name, IkarusErrorData * error_out); /// \brief Finds a property by a given name. /// \param project The project to search. From 52dead0c064d8b609c39c4bd45d00372e5e9d459 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 30 Jan 2024 09:44:55 +0100 Subject: [PATCH 048/166] implement ikarus_project_get_*_by_name functions Signed-off-by: Folling --- include/ikarus/persistence/project.h | 12 +++--- src/ikarus/objects/blueprint.cpp | 2 +- src/ikarus/objects/entity.cpp | 2 +- src/ikarus/objects/properties/util.hpp | 2 +- src/ikarus/objects/util.hpp | 23 +++++----- src/ikarus/persistence/project.cpp | 58 ++++++++++++++++++++++++++ 6 files changed, 80 insertions(+), 19 deletions(-) diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 7847a78..d0116a4 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -89,7 +89,7 @@ IKA_API char const * ikarus_project_get_path(IkarusProject const * project, Ikar /// \param entities_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_entities( - IkarusProject const * project, + IkarusProject * project, struct IkarusEntity ** entities_out, size_t entities_out_size, IkarusErrorData * error_out @@ -112,7 +112,7 @@ IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, Ik /// \param blueprints_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_blueprints( - IkarusProject const * project, + IkarusProject * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size, IkarusErrorData * error_out @@ -124,7 +124,7 @@ IKA_API void ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject * project, IkarusErrorData * error_out); /// \brief Finds an entity by a given name. /// \param project The project to search. @@ -135,7 +135,7 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The entity with the given name or null if none was found. -IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject const * project, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Finds a property by a given name. /// \param project The project to search. @@ -151,7 +151,7 @@ IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject const * project, /// \param error_out \see errors.h /// \return The property with the given name or null if none was found. IKA_API struct IkarusProperty * -get_property_by_name(IkarusProject const * project, struct IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out); +get_property_by_name_and_scope(IkarusProject * project, struct IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out); /// \brief Finds a blueprint by a given name. /// \param project The project to search. @@ -162,7 +162,7 @@ get_property_by_name(IkarusProject const * project, struct IkarusPropertyScope * /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The blueprint with the given name or null if none was found. -IKA_API struct IkarusBlueprint * get_blueprints_by_name(IkarusProject const * project, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index 326129d..dde1193 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -17,7 +17,7 @@ IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, project, nullptr, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, nullptr, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 94e8b75..0b3da7f 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -12,7 +12,7 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, project, nullptr, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, nullptr, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/properties/util.hpp b/src/ikarus/objects/properties/util.hpp index 092296e..2af0e32 100644 --- a/src/ikarus/objects/properties/util.hpp +++ b/src/ikarus/objects/properties/util.hpp @@ -23,7 +23,7 @@ T * create_property( IKARUS_FAIL_IF_NULL(project, nullptr); IKARUS_FAIL_IF_NULL(property_scope, nullptr); IKARUS_FAIL_IF_PROPERTY_SCOPE_INVALID(property_scope, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, project, property_scope, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, property_scope, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp index 29857cf..d0b9fa2 100644 --- a/src/ikarus/objects/util.hpp +++ b/src/ikarus/objects/util.hpp @@ -96,15 +96,18 @@ name_is_unique(IkarusProject * project, std::string_view name, IkarusProperty co return name_is_unique(project, name, scope.get(), error_out); } -#define IKARUS_FAIL_IF_NAME_INVALID(name, project, object, ret, ...) \ - IKARUS_FAIL_IF_NULL(name, ret); \ - IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); \ - IKARUS_FAIL_IF( \ - !ikarus::util::name_is_unique(project, name, object, error_out), \ - ret, \ - "name must be unique", \ - IkarusErrorInfo_Client_InvalidInput \ - ); \ +#define IKARUS_FAIL_IF_NAME_INVALID(name, ret) \ + IKARUS_FAIL_IF_NULL(name, ret); \ + IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); + +#define IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, object, ret, ...) \ + IKARUS_FAIL_IF_NAME_INVALID(name, ret); \ + IKARUS_FAIL_IF( \ + !ikarus::util::name_is_unique(project, name, object, error_out), \ + ret, \ + "name must be unique", \ + IkarusErrorInfo_Client_InvalidInput \ + ); \ IKARUS_FAIL_IF_ERROR(ret); template @@ -112,7 +115,7 @@ void object_set_name(O * object, char const * name, IkarusErrorData * error_out) IKARUS_FAIL_IF_NULL(object, ); IKARUS_FAIL_IF_OBJECT_MISSING(object, ); IKARUS_FAIL_IF_NULL(name, ); - IKARUS_FAIL_IF_NAME_INVALID(name, object->project, object, ); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, object->project, object, ); IKARUS_TRYRV_OR_FAIL( , diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 6c460f7..b7c3e89 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -275,3 +276,60 @@ size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusErrorData return ret; } + +struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + // TODO, 'InvalidInput' doesn't really make sense here, we'd need to adjust the macros to support distinguishing between different + // errors. In this case `sqlitecpp::MissingRow` and database related errors. Same for the other functions. + IKARUS_VTRYRV_OR_FAIL( + auto const id, + nullptr, + "unable to find entity in database: {}", + IkarusErrorInfo_Client_InvalidInput, + project->db->query_one("SELECT `id` FROM `entities` WHERE `name` = ?", name) + ); + + return project->get_entity(id); +} + +struct IkarusProperty * +get_property_by_name(IkarusProject * project, IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto const id_and_type, + nullptr, + "unable to find property in database: {}", + IkarusErrorInfo_Client_InvalidInput, + project->db->query_one( + "SELECT `id`, `type` FROM `properties` WHERE `name` = ? AND `scope` = ?", + name, + scope->get_id() + ) + ); + + auto const [id, type] = id_and_type; + + return project->get_property(id, type); +} + +IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto const id, + nullptr, + "unable to find blueprint in database: {}", + IkarusErrorInfo_Client_InvalidInput, + project->db->query_one("SELECT `id` FROM `blueprints` WHERE `name` = ?", name) + ); + + return project->get_blueprint(id); +} From f7361c657eaebd02ce8d76dfd8230cb889d42df9 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 30 Jan 2024 09:46:19 +0100 Subject: [PATCH 049/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index eadf323..3904483 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit eadf3237bfa853763b332777e9dc0f16df8cca71 +Subproject commit 39044837cd3b332aa874325ef36e5df033b092c3 From 4386903e59f61cb3faecdfb26e6e71cf15f9ec27 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 1 Feb 2024 11:15:32 +0100 Subject: [PATCH 050/166] rename project object_by_name_functions with ikarus_project prefix Signed-off-by: Folling --- include/ikarus/persistence/project.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index d0116a4..acd113a 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -135,7 +135,7 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject * project, Ikaru /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The entity with the given name or null if none was found. -IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusEntity * ikarus_project_get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Finds a property by a given name. /// \param project The project to search. @@ -150,8 +150,12 @@ IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject * project, char c /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The property with the given name or null if none was found. -IKA_API struct IkarusProperty * -get_property_by_name_and_scope(IkarusProject * project, struct IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusProperty * ikarus_project_get_property_by_name_and_scope( + IkarusProject * project, + struct IkarusPropertyScope * scope, + char const * name, + IkarusErrorData * error_out +); /// \brief Finds a blueprint by a given name. /// \param project The project to search. @@ -162,7 +166,8 @@ get_property_by_name_and_scope(IkarusProject * project, struct IkarusPropertySco /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The blueprint with the given name or null if none was found. -IKA_API struct IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusBlueprint * +ikarus_project_get_blueprint_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); IKARUS_END_HEADER From 61a3eb7a8af1e6800f99df484d91d159e0934861 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 1 Feb 2024 19:51:37 +0100 Subject: [PATCH 051/166] unmodernize id.cpp Signed-off-by: Folling --- src/ikarus/id.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ikarus/id.cpp b/src/ikarus/id.cpp index 4269215..9749667 100644 --- a/src/ikarus/id.cpp +++ b/src/ikarus/id.cpp @@ -2,13 +2,13 @@ #include -constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; -constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; +uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; +uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; -auto ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { +IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) { return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); } -auto ikarus_id_get_object_type(IkarusId id) -> IkarusObjectType { +IkarusObjectType ikarus_id_get_object_type(IkarusId id) { return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); } From c5cd74b3d6fafbc6eaf8c3ab9741a22d2742b838 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 10 Feb 2024 15:44:20 +0100 Subject: [PATCH 052/166] fixup cppbase::visit overloaded ctor usage Signed-off-by: Folling --- src/ikarus/objects/properties/property_scope.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ikarus/objects/properties/property_scope.cpp b/src/ikarus/objects/properties/property_scope.cpp index a0b9ade..b8ec32f 100644 --- a/src/ikarus/objects/properties/property_scope.cpp +++ b/src/ikarus/objects/properties/property_scope.cpp @@ -48,10 +48,10 @@ void ikarus_property_source_visit_const( void * user_data ) { std::visit( - cppbase::overloaded( + cppbase::overloaded{ [blueprint_visitor, user_data](IkarusBlueprint const * blueprint) { blueprint_visitor(blueprint, user_data); }, [entity_visitor, user_data](IkarusEntity const * entity) { entity_visitor(entity, user_data); } - ), + }, property_source->data ); } From 15ce4b79517ea8b5eac5f2244a359c8860d0d7a1 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 10 Feb 2024 15:55:11 +0100 Subject: [PATCH 053/166] fixup Boost_INCLUDE_DIRS -= S Signed-off-by: Folling --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01e5f44..8fe8e89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries( target_include_directories( ikarus PRIVATE - ${Boost_INCLUDE_DIRS} + ${Boost_INCLUDE_DIR} ) if (LIBIKARUS_ENABLE_LINTS) @@ -91,4 +91,4 @@ if (LIBIKARUS_BUILD_DOCS) libikarus_tests libikarus_docs ) -endif () \ No newline at end of file +endif () From 83445b3d94e2720ff218d14ed24ca3a3d1c44835 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 11 Feb 2024 15:23:18 +0100 Subject: [PATCH 054/166] fixup constness of IkarusProject in ikarus_project_get_entity_count Signed-off-by: Folling --- include/ikarus/persistence/project.h | 2 +- src/ikarus/persistence/project.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index acd113a..18a35f5 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -124,7 +124,7 @@ IKA_API void ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Finds an entity by a given name. /// \param project The project to search. diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index b7c3e89..9c264f1 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -263,7 +263,7 @@ void ikarus_project_get_entities( } } -size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusErrorData * error_out) { +size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, 0); IKARUS_VTRYRV_OR_FAIL( From 31567c341a8af81496fac8b825ddab3cb5976497 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 11 Feb 2024 16:39:07 +0100 Subject: [PATCH 055/166] fixup inclusion of header in entity.cpp Signed-off-by: Folling --- src/ikarus/objects/entity.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 0b3da7f..9e791a7 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -1,9 +1,10 @@ -#include "entity.hpp" +#include "ikarus/objects/entity.h" #include #include #include +#include #include #include #include From 43482aa2bc7c3ba80219ef9aa7274feb4e590669 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 10:44:05 +0100 Subject: [PATCH 056/166] add module.modulemap Signed-off-by: Folling --- include/module.modulemap | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 include/module.modulemap diff --git a/include/module.modulemap b/include/module.modulemap new file mode 100644 index 0000000..6b2dbcb --- /dev/null +++ b/include/module.modulemap @@ -0,0 +1,6 @@ +module ikarus { + header "ikarus/persistence/project.h" + header "ikarus/objects/entity.h" + header "ikarus/objects/blueprint.h" + export * +} \ No newline at end of file From e0d1d8bb6fc056dd1cb12a1cda2d61ff58e56a9f Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 12:06:22 +0100 Subject: [PATCH 057/166] expose id getters Signed-off-by: Folling --- include/ikarus/objects/blueprint.h | 9 +++++++++ include/ikarus/objects/entity.h | 9 +++++++++ include/ikarus/objects/properties/property.h | 17 +++++++++++++++++ src/ikarus/objects/blueprint.cpp | 4 ++++ src/ikarus/objects/entity.cpp | 4 ++++ src/ikarus/objects/properties/property.cpp | 4 ++++ src/ikarus/objects/util.hpp | 8 ++++++++ 7 files changed, 55 insertions(+) diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index adfacd4..ec2087f 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -4,6 +4,7 @@ /// \author Folling #include +#include #include #include @@ -38,6 +39,14 @@ IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project /// \remark The blueprint must not be accessed after deletion. IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * error_out); +/// \brief Gets the ID of a blueprint. +/// \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 IkarusId 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. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 53abc70..04d59b0 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -54,6 +55,14 @@ IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char /// \remark The entity must not be accessed after deletion. IKA_API void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); +/// \brief Gets the ID of an entity. +/// \param entity The entity 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 entity or 0 if an error occurs. +IKA_API IkarusId ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out); + /// \brief Gets the project an entity is part of. /// \param entity The entity to get the project of. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index 589854f..aa52b2c 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -4,6 +4,7 @@ /// \author Folling #include +#include #include #include #include @@ -64,6 +65,22 @@ struct IkarusProperty; /// \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 IkarusId 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 type info of a property. /// \param property The property to get the type info of. /// \pre \li Must not be null. diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index dde1193..aa5f35c 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -49,6 +49,10 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * erro blueprint->project->uncache(blueprint); } +IkarusId 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); } diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 9e791a7..d724259 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -45,6 +45,10 @@ void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { entity->project->uncache(entity); } +IkarusId 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); } diff --git a/src/ikarus/objects/properties/property.cpp b/src/ikarus/objects/properties/property.cpp index f45adbb..929d3a6 100644 --- a/src/ikarus/objects/properties/property.cpp +++ b/src/ikarus/objects/properties/property.cpp @@ -28,6 +28,10 @@ IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * property->project->uncache(property); } +IkarusId 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); } diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp index d0b9fa2..8e037ca 100644 --- a/src/ikarus/objects/util.hpp +++ b/src/ikarus/objects/util.hpp @@ -16,6 +16,14 @@ namespace ikarus::util { +template +[[nodiscard]] IkarusId 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 [[nodiscard]] IkarusProject * object_get_project(O const * object, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(object, nullptr); From ecc176caf2347d7b3a6b9493de6a23983d0c11fa Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 15:21:25 +0100 Subject: [PATCH 058/166] fixup missing ikarus_ prefix for error_info_name Signed-off-by: Folling --- include/ikarus/errors.h | 2 +- src/ikarus/errors.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index ce11788..a7a4fb4 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -107,7 +107,7 @@ struct IkarusErrorData { /// \param info The error info to get the name of. /// \return The name of the error info. /// \remark The returned pointer is valid for the lifetime of the program and must not be freed. -IKA_API char const * get_error_info_name(IkarusErrorInfo info); +IKA_API char const * ikarus_get_error_info_name(IkarusErrorInfo info); /// \brief Checks if an error data is a success. /// \param data The error data to check. diff --git a/src/ikarus/errors.cpp b/src/ikarus/errors.cpp index d98b28c..35014f8 100644 --- a/src/ikarus/errors.cpp +++ b/src/ikarus/errors.cpp @@ -8,7 +8,7 @@ #include -char const * get_error_info_name(IkarusErrorInfo info) { +char const * ikarus_get_error_info_name(IkarusErrorInfo info) { switch (info) { case IkarusErrorInfo_None: return "None"; From dffdef67280112f7d4891528618fead7c6747a0f Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 15:36:44 +0100 Subject: [PATCH 059/166] invert condition for whether name is unique Signed-off-by: Folling --- src/ikarus/objects/util.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp index 8e037ca..4b55caa 100644 --- a/src/ikarus/objects/util.hpp +++ b/src/ikarus/objects/util.hpp @@ -63,7 +63,7 @@ template project->db->query_one("SELECT EXISTS(SELECT 1 FROM `blueprints` WHERE `name` = ?)", name) ); - return exists; + return !exists; } [[nodiscard]] inline bool name_is_unique( @@ -80,7 +80,7 @@ template project->db->query_one("SELECT EXISTS(SELECT 1 FROM `entities` WHERE `name` = ?)", name) ); - return exists; + return !exists; } [[nodiscard]] inline bool @@ -93,7 +93,7 @@ name_is_unique(IkarusProject const * project, std::string_view name, IkarusPrope project->db->query_one("SELECT EXISTS(SELECT 1 FROM `properties` WHERE `name` = ? AND `scope` = ?)", name, scope->get_id()) ); - return exists; + return !exists; } [[nodiscard]] inline bool From d0a0151db784ec55271d62a2cb657fb976380bad Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 15:49:26 +0100 Subject: [PATCH 060/166] fixup name-duplication checking Signed-off-by: Folling --- src/ikarus/objects/blueprint.cpp | 2 +- src/ikarus/objects/entity.cpp | 2 +- src/ikarus/objects/util.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index aa5f35c..77ffb70 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -17,7 +17,7 @@ IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, nullptr, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index d724259..bd5aa25 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -13,7 +13,7 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, nullptr, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp index 4b55caa..1de035a 100644 --- a/src/ikarus/objects/util.hpp +++ b/src/ikarus/objects/util.hpp @@ -97,7 +97,7 @@ name_is_unique(IkarusProject const * project, std::string_view name, IkarusPrope } [[nodiscard]] inline bool -name_is_unique(IkarusProject * project, std::string_view name, IkarusProperty const * property, IkarusErrorData * error_out) { +name_is_unique(IkarusProject const * project, std::string_view name, IkarusProperty const * property, IkarusErrorData * error_out) { std::unique_ptr scope{ikarus_property_get_scope(property, error_out)}; IKARUS_FAIL_IF_ERROR(false); From eb4c84f86d3f52d6e6a5ade24737296b8d28a218 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 15:52:08 +0100 Subject: [PATCH 061/166] fixup object creation queries Signed-off-by: Folling --- src/ikarus/objects/blueprint.cpp | 4 ++-- src/ikarus/objects/entity.cpp | 6 +++--- src/ikarus/objects/properties/util.hpp | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index 77ffb70..a1cf9e5 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -25,9 +25,9 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c "failed to create blueprint: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Blueprint, name, "")); + TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?)", IkarusObjectType_Blueprint)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id)); + TRY(db->execute("INSERT INTO `blueprints`(`id`, `name`) VALUES(?, ?)", id, name)); return cppbase::ok(id); }) ); diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index bd5aa25..150b91f 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -13,7 +13,7 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, @@ -21,9 +21,9 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * "failed to create entity: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?, ?, ?)", IkarusObjectType_Entity)); + TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?)", IkarusObjectType_Entity)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Entity); - TRY(db->execute("INSERT INTO `entities`(`id`, `name`) VALUES(?)", id, name)); + TRY(db->execute("INSERT INTO `entities`(`id`, `name`) VALUES(?, ?)", id, name)); return cppbase::ok(id); }) ); diff --git a/src/ikarus/objects/properties/util.hpp b/src/ikarus/objects/properties/util.hpp index 2af0e32..c095a18 100644 --- a/src/ikarus/objects/properties/util.hpp +++ b/src/ikarus/objects/properties/util.hpp @@ -31,11 +31,12 @@ T * create_property( "failed to create property: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name, property_scope](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Property, name, "")); + 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`, `type`, `source`) VALUES(?, ?, ?)", + "INSERT INTO `properties`(`id`, `name`, `type`, `source`) VALUES(?, ?, ?, ?)", id, + name, T::PropertyType, property_scope->get_id() )); From 461694c6618912896b8f8a17188318075d1d277a Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 16:02:51 +0100 Subject: [PATCH 062/166] fixup entity deletion query Signed-off-by: Folling --- src/ikarus/objects/entity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 150b91f..e1dbf5a 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -39,7 +39,7 @@ void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { , "unable to delete entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->execute("DELETE FROM `objects` WHERE `id` == ?", entity->id) + entity->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", entity->id) ); entity->project->uncache(entity); From b4004d405063494d6ca7d0e8413a1f0332df95b3 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 16:10:41 +0100 Subject: [PATCH 063/166] print error message when unable to check for project path existence Signed-off-by: Folling --- src/ikarus/persistence/project.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 9c264f1..b2e92f8 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -70,7 +70,12 @@ IkarusProject * ikarus_project_create(char const * path, char const * name, Ikar boost::system::error_code ec; bool const exists = fs::exists(fs_path, ec); - IKARUS_FAIL_IF(ec, nullptr, "unable to check whether path is occupied", IkarusErrorInfo_Filesystem_AlreadyExists); + IKARUS_FAIL_IF( + ec, + 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); } From c4b06c09d1ce4297e809c74ae89617664ea020a4 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 16:23:01 +0100 Subject: [PATCH 064/166] filter ENOENT from boost::error_code check Signed-off-by: Folling --- src/ikarus/persistence/project.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index b2e92f8..87ebef5 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -69,9 +69,8 @@ IkarusProject * ikarus_project_create(char const * path, char const * name, Ikar { boost::system::error_code ec; bool const exists = fs::exists(fs_path, ec); - IKARUS_FAIL_IF( - ec, + 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 From 23e38156d74c95847451e18eca1b49b3880b0575 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 16:39:44 +0100 Subject: [PATCH 065/166] update sqlitecpp Signed-off-by: Folling --- src/ikarus/persistence/migrations/m0_initial_layout.sql | 2 +- vendor/sqlitecpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index 0031cee..9bf3cc7 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -48,7 +48,7 @@ CREATE TABLE `properties` `source` INT NOT NULL, PRIMARY KEY (`id`), - UNIQUE(`source`, `name`), + UNIQUE (`source`, `name`), FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE, FOREIGN KEY (`source`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 3904483..7832ea6 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 39044837cd3b332aa874325ef36e5df033b092c3 +Subproject commit 7832ea68dca4c8367dd938b079a01ccffe6a7acd From 2c09ff48caca34f77ebb0b35292fcd66f47a1305 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 15 Feb 2024 22:04:37 +0100 Subject: [PATCH 066/166] improve cmake setup Signed-off-by: Folling --- .gitmodules | 3 +++ CMakeLists.txt | 29 +++++++---------------------- vendor/CMakeLists.txt | 3 ++- vendor/catch2 | 1 - vendor/cppbase | 1 + vendor/sqlitecpp | 2 +- 6 files changed, 14 insertions(+), 25 deletions(-) delete mode 160000 vendor/catch2 create mode 160000 vendor/cppbase diff --git a/.gitmodules b/.gitmodules index a26cc62..933638b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "vendor/doxygen-awesome-css"] path = vendor/doxygen-awesome-css url = git@github.com:jothepro/doxygen-awesome-css.git +[submodule "vendor/cppbase"] + path = vendor/cppbase + url = ssh://git@git.rewritesarebliss.com:16658/Folling/cppbase.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fe8e89..92e04cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,13 +5,6 @@ option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF) option(LIBIKARUS_ENABLE_LINTS "Enable linting" OFF) option(LIBIKARUS_BUILD_DOCS "Build documentation" OFF) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_STANDARD 23) - -set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) - -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - add_subdirectory(vendor) add_subdirectory(include) add_subdirectory(src) @@ -24,6 +17,13 @@ add_library( ${SOURCE_FILES} ) +set_target_properties( + ikarus PROPERTIES + CXX_STANDARD 23 + CXX_STANDARD_REQUIRED ON + POSITION_INDEPENDENT_CODE TRUE +) + target_include_directories( ikarus PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include @@ -57,21 +57,6 @@ if (LIBIKARUS_ENABLE_LINTS) ) endif () -if (LIBIKARUS_ENABLE_TESTS) - add_executable(libikarus_tests ${SOURCE_FILES}) - target_link_libraries(libikarus_tests PRIVATE sqlitecpp Catch2::Catch2WithMain) - - target_include_directories( - libikarus_tests PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/include - ${CMAKE_CURRENT_LIST_DIR}/src - ) - - include(CTest) - include(vendor/catch2/extras/Catch.cmake) - catch_discover_tests(libikarus_tests) -endif () - if (LIBIKARUS_BUILD_DOCS) find_program(DOXYGEN_PATH NAMES doxygen REQUIRED) add_custom_target( diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 9820477..611db2e 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -1,2 +1,3 @@ -add_subdirectory(catch2) +# order is important here since sqlitecpp otherwise duplicates cppbase +add_subdirectory(cppbase) add_subdirectory(sqlitecpp) diff --git a/vendor/catch2 b/vendor/catch2 deleted file mode 160000 index 5bba3e4..0000000 --- a/vendor/catch2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5bba3e4038602badb691da914523f667a2dd1f27 diff --git a/vendor/cppbase b/vendor/cppbase new file mode 160000 index 0000000..6bf80cf --- /dev/null +++ b/vendor/cppbase @@ -0,0 +1 @@ +Subproject commit 6bf80cf9d5ff54ab300f7e88a8cb9996f441dae6 diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 7832ea6..4a97c47 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 7832ea68dca4c8367dd938b079a01ccffe6a7acd +Subproject commit 4a97c47847cbe9cf610a636137f678b447cdde5a From 7e883d24eb991cdd8b0f5e52edb23fc7b3186179 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 15 Feb 2024 23:30:54 +0100 Subject: [PATCH 067/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 4a97c47..32fa3c1 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 4a97c47847cbe9cf610a636137f678b447cdde5a +Subproject commit 32fa3c1a9beae1a2c49fdd08cc659ea6153da666 From 98cb7a44eff13d9e11685aec5a6fac88606391d8 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 12 May 2024 14:15:42 +0200 Subject: [PATCH 068/166] add flatbuffers support and initial rewrite Signed-off-by: Folling --- .clang-format | 6 +- CMakeLists.txt | 10 +- include/CMakeLists.txt | 1 + include/ikarus/CMakeLists.txt | 1 + include/ikarus/errors.h | 5 +- include/ikarus/global.h | 20 - include/ikarus/id.h | 46 - include/ikarus/macros.h | 34 +- include/ikarus/models/CMakeLists.txt | 41 + include/ikarus/models/blueprint.fbs | 12 + include/ikarus/models/blueprint_generated.h | 153 ++ include/ikarus/models/entity.fbs | 26 + include/ikarus/models/entity_generated.h | 398 ++++ include/ikarus/models/property.fbs | 14 + include/ikarus/models/property_generated.h | 289 +++ include/ikarus/models/value.fbs | 85 + include/ikarus/models/value_generated.h | 1719 +++++++++++++++++ include/ikarus/objects/blueprint.h | 60 +- include/ikarus/objects/entity.h | 205 +- include/ikarus/objects/object.h | 50 - include/ikarus/objects/object_type.h | 34 - .../objects/properties/number_property.h | 26 - include/ikarus/objects/properties/property.h | 118 +- .../objects/properties/property_scope.h | 62 - .../ikarus/objects/properties/property_type.h | 27 - .../ikarus/objects/properties/text_property.h | 27 +- .../objects/properties/toggle_property.h | 31 +- include/ikarus/persistence/project.h | 77 +- include/ikarus/values/entity_property_value.h | 46 - include/ikarus/values/number_value.h | 54 +- include/ikarus/values/text_value.h | 69 +- include/ikarus/values/toggle_value.h | 63 +- include/ikarus/values/value.h | 53 +- include/ikarus/values/value_cardinality.h | 23 + include/ikarus/values/value_type.h | 198 ++ src/ikarus/errors.cpp | 8 - src/ikarus/errors.hpp | 13 - src/ikarus/global.cpp | 7 - src/ikarus/id.cpp | 14 - src/ikarus/objects/blueprint.cpp | 38 +- src/ikarus/objects/blueprint.hpp | 8 +- src/ikarus/objects/entity.cpp | 307 ++- src/ikarus/objects/entity.hpp | 9 +- src/ikarus/objects/object.cpp | 88 +- src/ikarus/objects/object.hpp | 21 +- src/ikarus/objects/object_type.cpp | 12 - .../objects/properties/number_property.cpp | 2 +- .../objects/properties/number_property.hpp | 8 +- src/ikarus/objects/properties/property.cpp | 20 +- src/ikarus/objects/properties/property.hpp | 4 +- .../objects/properties/property_scope.cpp | 57 - .../objects/properties/property_scope.hpp | 39 - .../objects/properties/text_property.cpp | 2 +- .../objects/properties/text_property.hpp | 8 +- .../objects/properties/toggle_property.cpp | 2 +- .../objects/properties/toggle_property.hpp | 11 +- src/ikarus/objects/properties/util.hpp | 7 +- src/ikarus/objects/util.hpp | 79 +- src/ikarus/persistence/migrations.hpp | 3 +- .../migrations/m0_initial_layout.sql | 87 +- src/ikarus/persistence/project.cpp | 26 +- src/ikarus/persistence/project.hpp | 21 +- src/ikarus/values/entity_property_value.hpp | 6 - src/ikarus/values/number_value.cpp | 56 +- src/ikarus/values/number_value.hpp | 2 +- src/ikarus/values/text_value.cpp | 56 +- src/ikarus/values/text_value.hpp | 2 +- src/ikarus/values/toggle_value.cpp | 56 +- src/ikarus/values/toggle_value.hpp | 2 +- src/ikarus/values/value.cpp | 78 +- src/ikarus/values/value.hpp | 28 +- src/ikarus/values/value_base.hpp | 62 +- 72 files changed, 3929 insertions(+), 1403 deletions(-) create mode 100644 include/ikarus/CMakeLists.txt delete mode 100644 include/ikarus/global.h delete mode 100644 include/ikarus/id.h create mode 100644 include/ikarus/models/CMakeLists.txt create mode 100644 include/ikarus/models/blueprint.fbs create mode 100644 include/ikarus/models/blueprint_generated.h create mode 100644 include/ikarus/models/entity.fbs create mode 100644 include/ikarus/models/entity_generated.h create mode 100644 include/ikarus/models/property.fbs create mode 100644 include/ikarus/models/property_generated.h create mode 100644 include/ikarus/models/value.fbs create mode 100644 include/ikarus/models/value_generated.h delete mode 100644 include/ikarus/objects/object.h delete mode 100644 include/ikarus/objects/object_type.h delete mode 100644 include/ikarus/objects/properties/property_scope.h delete mode 100644 include/ikarus/objects/properties/property_type.h delete mode 100644 include/ikarus/values/entity_property_value.h create mode 100644 include/ikarus/values/value_cardinality.h create mode 100644 include/ikarus/values/value_type.h delete mode 100644 src/ikarus/global.cpp delete mode 100644 src/ikarus/id.cpp delete mode 100644 src/ikarus/objects/object_type.cpp delete mode 100644 src/ikarus/objects/properties/property_scope.cpp delete mode 100644 src/ikarus/objects/properties/property_scope.hpp diff --git a/.clang-format b/.clang-format index 7d8a46d..85337cc 100644 --- a/.clang-format +++ b/.clang-format @@ -64,9 +64,9 @@ BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon -BreakStringLiterals: false +BreakStringLiterals: true -ColumnLimit: 140 +ColumnLimit: 80 CommentPragmas: '^\\.+' CompactNamespaces: false ConstructorInitializerIndentWidth: 4 @@ -117,7 +117,7 @@ IndentCaseBlocks: false IndentCaseLabels: false IndentExternBlock: NoIndent IndentGotoLabels: false -IndentPPDirectives: None +IndentPPDirectives: BeforeHash IndentRequiresClause: true IndentWidth: 4 IndentWrappedFunctionNames: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 92e04cc..8a16b08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,11 @@ add_library( ${SOURCE_FILES} ) +add_dependencies( + ikarus + flatbuffer_headers +) + set_target_properties( ikarus PROPERTIES CXX_STANDARD 23 @@ -71,9 +76,4 @@ if (LIBIKARUS_BUILD_DOCS) ikarus libikarus_docs ) - - add_dependencies( - libikarus_tests - libikarus_docs - ) endif () diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 2e91ee9..e3043c7 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -6,3 +6,4 @@ file( set(INCLUDE_FILES ${FILES} PARENT_SCOPE) +add_subdirectory(ikarus) \ No newline at end of file diff --git a/include/ikarus/CMakeLists.txt b/include/ikarus/CMakeLists.txt new file mode 100644 index 0000000..cd981b8 --- /dev/null +++ b/include/ikarus/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(models) \ No newline at end of file diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index a7a4fb4..926f07d 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -4,11 +4,10 @@ /// \author Folling #include -#include /// \addtogroup errors Errors /// \brief Error handling within libikarus -/// \details Functions in Ikarus may fail, in which case they 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. /// If the out parameter is null nothing will be stored. This is not recommended as it essentially ignores errors. /// For the sake of simplicity we have avoided mechanisms that "force" clients to handle errors. @@ -93,7 +92,7 @@ enum IkarusErrorInfo { }; /// \brief The maximum length of an error message. -size_t const IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT = 128; +#define IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT 128 /// \brief The data stored for an error struct IkarusErrorData { diff --git a/include/ikarus/global.h b/include/ikarus/global.h deleted file mode 100644 index 4665863..0000000 --- a/include/ikarus/global.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -/// \file global.h -/// \author Folling - -#include - -/// \defgroup global Global -/// \brief Information relevant to the entire library. -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief Frees a pointer allocated by ikarus. Every pointer returned by a function must be freed using this function -/// unless explicitly stated otherwise. -IKA_API void ikarus_free(void * ptr); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/id.h b/include/ikarus/id.h deleted file mode 100644 index c76e341..0000000 --- a/include/ikarus/id.h +++ /dev/null @@ -1,46 +0,0 @@ -// IMPLEMENTATION_DETAIL_DATABASE - -/// \file id.h -/// \author Folling - -/// \privatesection - -#pragma once - -#include -#include -#include - -/// \defgroup id Ids -/// \brief Ids are used to identify objects in the database. -/// \details They are stored as 64 bit integers with the following layout: -/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. -/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. -/// - next 7 bits: #IkarusObjectType -/// - last 56 bits: incremented counter generated by the database -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief A wrapper around a 64 bit integer that represents the id of an object. -/// \details They are stored as 64 bit integers with the following layout: -/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. -/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. -/// - next 7 bits: #IkarusObjectType -/// - last 56 bits: incremented counter generated by the database -typedef int64_t IkarusId; - -/// \brief Creates an id from the given data and type. -/// \param data The data to use for the id. -/// \param type The type to use for the id. -/// \return The created id. -IKA_API IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type); - -/// \brief Fetches the object type of the given id. -/// \param id The id to fetch the object type for. -/// \return The object type of the given id. -IKA_API IkarusObjectType ikarus_id_get_object_type(IkarusId id); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/macros.h b/include/ikarus/macros.h index fdc6510..e7714dc 100644 --- a/include/ikarus/macros.h +++ b/include/ikarus/macros.h @@ -1,27 +1,23 @@ #pragma once -#if defined(__unix__) - -#define IKA_OS_FAMILY_UNIX -#define IKA_API __attribute__((visibility("default"))) - -#if defined(linux) -#define IKA_OS_LINUX -#endif - -#elif defined(_WIN32) || defined(WIN32) -#define IKA_OS_WIN -#define IKA_API __declspec(dllexport) -#endif - -#ifndef IKA_API #define IKA_API + +#if defined(__unix__) + #define IKA_OS_FAMILY_UNIX + #define IKA_API __attribute__((visibility("default"))) + + #if defined(linux) + #define IKA_OS_LINUX + #endif +#elif defined(_WIN32) + #define IKA_OS_WIN + #define IKA_API __declspec(dllexport) #endif #ifdef __cplusplus -#define IKARUS_BEGIN_HEADER extern "C" { -#define IKARUS_END_HEADER } + #define IKARUS_BEGIN_HEADER extern "C" { + #define IKARUS_END_HEADER } #else -#define IKARUS_BEGIN_HEADER -#define IKARUS_END_HEADER + #define IKARUS_BEGIN_HEADER + #define IKARUS_END_HEADER #endif diff --git a/include/ikarus/models/CMakeLists.txt b/include/ikarus/models/CMakeLists.txt new file mode 100644 index 0000000..e3d8758 --- /dev/null +++ b/include/ikarus/models/CMakeLists.txt @@ -0,0 +1,41 @@ +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 +) diff --git a/include/ikarus/models/blueprint.fbs b/include/ikarus/models/blueprint.fbs new file mode 100644 index 0000000..fb782d8 --- /dev/null +++ b/include/ikarus/models/blueprint.fbs @@ -0,0 +1,12 @@ +include "property.fbs"; + +namespace Ikarus; + +table Blueprint { + id: int64; + name: string; + description: string; + tags: [string]; +} + +root_type Blueprint; diff --git a/include/ikarus/models/blueprint_generated.h b/include/ikarus/models/blueprint_generated.h new file mode 100644 index 0000000..b70924b --- /dev/null +++ b/include/ikarus/models/blueprint_generated.h @@ -0,0 +1,153 @@ +// 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(VT_ID, 0); + } + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + const ::flatbuffers::String *description() const { + return GetPointer(VT_DESCRIPTION); + } + const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { + return GetPointer> *>(VT_TAGS); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(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(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 Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset 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 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(buf); +} + +inline const Ikarus::Blueprint *GetSizePrefixedBlueprint(const void *buf) { + return ::flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyBlueprintBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedBlueprintBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishBlueprintBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedBlueprintBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace Ikarus + +#endif // FLATBUFFERS_GENERATED_BLUEPRINT_IKARUS_H_ diff --git a/include/ikarus/models/entity.fbs b/include/ikarus/models/entity.fbs new file mode 100644 index 0000000..bce2ff5 --- /dev/null +++ b/include/ikarus/models/entity.fbs @@ -0,0 +1,26 @@ +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; diff --git a/include/ikarus/models/entity_generated.h b/include/ikarus/models/entity_generated.h new file mode 100644 index 0000000..e253038 --- /dev/null +++ b/include/ikarus/models/entity_generated.h @@ -0,0 +1,398 @@ +// 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(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 + 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(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 value) { + fbb_.AddOffset(NamedValue::VT_VALUE, value); + } + explicit NamedValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + fbb_.Required(o, NamedValue::VT_NAME); + return o; + } +}; + +inline ::flatbuffers::Offset CreateNamedValue( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, + ::flatbuffers::Offset 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 CreateNamedValueDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + ::flatbuffers::Offset 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(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(property_id() > _property_id) - static_cast(property_id() < _property_id); + } + Ikarus::Value::Data data_type() const { + return static_cast(GetField(VT_DATA_TYPE, 0)); + } + const void *data() const { + return GetPointer(VT_DATA); + } + template const T *data_as() const; + const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { + return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { + return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { + return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::SimpleData *data_as_SimpleData() const { + return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::CombinedData *data_as_CombinedData() const { + return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ListData *data_as_ListData() const { + return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ComplexData *data_as_ComplexData() const { + return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_PROPERTY_ID, 8) && + VerifyField(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() const { + return data_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *PropertyValue::data_as() const { + return data_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *PropertyValue::data_as() const { + return data_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *PropertyValue::data_as() const { + return data_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *PropertyValue::data_as() const { + return data_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *PropertyValue::data_as() const { + return data_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *PropertyValue::data_as() 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(PropertyValue::VT_PROPERTY_ID, property_id, 0); + } + void add_data_type(Ikarus::Value::Data data_type) { + fbb_.AddElement(PropertyValue::VT_DATA_TYPE, static_cast(data_type), 0); + } + void add_data(::flatbuffers::Offset data) { + fbb_.AddOffset(PropertyValue::VT_DATA, data); + } + explicit PropertyValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreatePropertyValue( + ::flatbuffers::FlatBufferBuilder &_fbb, + int64_t property_id = 0, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset 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(VT_ID, 0); + } + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + const ::flatbuffers::String *description() const { + return GetPointer(VT_DESCRIPTION); + } + const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { + return GetPointer> *>(VT_TAGS); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *values() const { + return GetPointer> *>(VT_VALUES); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *property_values() const { + return GetPointer> *>(VT_PROPERTY_VALUES); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(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(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>> values) { + fbb_.AddOffset(Entity::VT_VALUES, values); + } + void add_property_values(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> property_values) { + fbb_.AddOffset(Entity::VT_PROPERTY_VALUES, property_values); + } + explicit EntityBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset 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>> values = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> 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 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> *values = nullptr, + std::vector<::flatbuffers::Offset> *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(values) : 0; + auto property_values__ = property_values ? _fbb.CreateVectorOfSortedTables(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(buf); +} + +inline const Ikarus::Entity *GetSizePrefixedEntity(const void *buf) { + return ::flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyEntityBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedEntityBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishEntityBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedEntityBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace Ikarus + +#endif // FLATBUFFERS_GENERATED_ENTITY_IKARUS_H_ diff --git a/include/ikarus/models/property.fbs b/include/ikarus/models/property.fbs new file mode 100644 index 0000000..8a96bb2 --- /dev/null +++ b/include/ikarus/models/property.fbs @@ -0,0 +1,14 @@ +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; diff --git a/include/ikarus/models/property_generated.h b/include/ikarus/models/property_generated.h new file mode 100644 index 0000000..1111bee --- /dev/null +++ b/include/ikarus/models/property_generated.h @@ -0,0 +1,289 @@ +// 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(VT_ID, 0); + } + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + const ::flatbuffers::String *description() const { + return GetPointer(VT_DESCRIPTION); + } + const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { + return GetPointer> *>(VT_TAGS); + } + Ikarus::Value::Schema value_schema_type() const { + return static_cast(GetField(VT_VALUE_SCHEMA_TYPE, 0)); + } + const void *value_schema() const { + return GetPointer(VT_VALUE_SCHEMA); + } + template 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(value_schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *value_schema_as_SimpleSchema() const { + return value_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(value_schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *value_schema_as_CombinedSchema() const { + return value_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(value_schema()) : nullptr; + } + const Ikarus::Value::ListSchema *value_schema_as_ListSchema() const { + return value_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(value_schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *value_schema_as_ComplexSchema() const { + return value_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(value_schema()) : nullptr; + } + Ikarus::Value::Data default_value_type() const { + return static_cast(GetField(VT_DEFAULT_VALUE_TYPE, 0)); + } + const void *default_value() const { + return GetPointer(VT_DEFAULT_VALUE); + } + template 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(default_value()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *default_value_as_NumberDataPoint() const { + return default_value_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(default_value()) : nullptr; + } + const Ikarus::Value::TextDataPoint *default_value_as_TextDataPoint() const { + return default_value_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(default_value()) : nullptr; + } + const Ikarus::Value::SimpleData *default_value_as_SimpleData() const { + return default_value_type() == Ikarus::Value::Data::SimpleData ? static_cast(default_value()) : nullptr; + } + const Ikarus::Value::CombinedData *default_value_as_CombinedData() const { + return default_value_type() == Ikarus::Value::Data::CombinedData ? static_cast(default_value()) : nullptr; + } + const Ikarus::Value::ListData *default_value_as_ListData() const { + return default_value_type() == Ikarus::Value::Data::ListData ? static_cast(default_value()) : nullptr; + } + const Ikarus::Value::ComplexData *default_value_as_ComplexData() const { + return default_value_type() == Ikarus::Value::Data::ComplexData ? static_cast(default_value()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(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(verifier, VT_VALUE_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_VALUE_SCHEMA) && + VerifySchema(verifier, value_schema(), value_schema_type()) && + VerifyField(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() const { + return value_schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *Property::value_schema_as() const { + return value_schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *Property::value_schema_as() const { + return value_schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *Property::value_schema_as() const { + return value_schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *Property::value_schema_as() const { + return value_schema_as_ComplexSchema(); +} + +template<> inline const Ikarus::Value::ToggleDataPoint *Property::default_value_as() const { + return default_value_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *Property::default_value_as() const { + return default_value_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *Property::default_value_as() const { + return default_value_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *Property::default_value_as() const { + return default_value_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *Property::default_value_as() const { + return default_value_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *Property::default_value_as() const { + return default_value_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *Property::default_value_as() 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(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(Property::VT_VALUE_SCHEMA_TYPE, static_cast(value_schema_type), 0); + } + void add_value_schema(::flatbuffers::Offset value_schema) { + fbb_.AddOffset(Property::VT_VALUE_SCHEMA, value_schema); + } + void add_default_value_type(Ikarus::Value::Data default_value_type) { + fbb_.AddElement(Property::VT_DEFAULT_VALUE_TYPE, static_cast(default_value_type), 0); + } + void add_default_value(::flatbuffers::Offset default_value) { + fbb_.AddOffset(Property::VT_DEFAULT_VALUE, default_value); + } + explicit PropertyBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset 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 value_schema = 0, + Ikarus::Value::Data default_value_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset 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 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 value_schema = 0, + Ikarus::Value::Data default_value_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset 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(buf); +} + +inline const Ikarus::Property *GetSizePrefixedProperty(const void *buf) { + return ::flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyPropertyBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedPropertyBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishPropertyBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedPropertyBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace Ikarus + +#endif // FLATBUFFERS_GENERATED_PROPERTY_IKARUS_H_ diff --git a/include/ikarus/models/value.fbs b/include/ikarus/models/value.fbs new file mode 100644 index 0000000..27a27e9 --- /dev/null +++ b/include/ikarus/models/value.fbs @@ -0,0 +1,85 @@ +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; diff --git a/include/ikarus/models/value_generated.h b/include/ikarus/models/value_generated.h new file mode 100644 index 0000000..c9da9e3 --- /dev/null +++ b/include/ikarus/models/value_generated.h @@ -0,0 +1,1719 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_H_ +#define FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_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"); + +namespace Ikarus { +namespace Value { + +struct ToggleDataPoint; +struct ToggleDataPointBuilder; + +struct NumberDataPoint; +struct NumberDataPointBuilder; + +struct TextDataPoint; +struct TextDataPointBuilder; + +struct ConstantSchema; +struct ConstantSchemaBuilder; + +struct SimpleSchema; +struct SimpleSchemaBuilder; + +struct SimpleData; +struct SimpleDataBuilder; + +struct CombinedSchema; +struct CombinedSchemaBuilder; + +struct CombinedData; +struct CombinedDataBuilder; + +struct ListSchema; +struct ListSchemaBuilder; + +struct ListData; +struct ListDataBuilder; + +struct NamedSchema; +struct NamedSchemaBuilder; + +struct ComplexSchema; +struct ComplexSchemaBuilder; + +struct NamedData; +struct NamedDataBuilder; + +struct ComplexData; +struct ComplexDataBuilder; + +struct Value; +struct ValueBuilder; + +enum class Data : uint8_t { + NONE = 0, + ToggleDataPoint = 1, + NumberDataPoint = 2, + TextDataPoint = 3, + SimpleData = 4, + CombinedData = 5, + ListData = 6, + ComplexData = 7, + MIN = NONE, + MAX = ComplexData +}; + +inline const Data (&EnumValuesData())[8] { + static const Data values[] = { + Data::NONE, + Data::ToggleDataPoint, + Data::NumberDataPoint, + Data::TextDataPoint, + Data::SimpleData, + Data::CombinedData, + Data::ListData, + Data::ComplexData + }; + return values; +} + +inline const char * const *EnumNamesData() { + static const char * const names[9] = { + "NONE", + "ToggleDataPoint", + "NumberDataPoint", + "TextDataPoint", + "SimpleData", + "CombinedData", + "ListData", + "ComplexData", + nullptr + }; + return names; +} + +inline const char *EnumNameData(Data e) { + if (::flatbuffers::IsOutRange(e, Data::NONE, Data::ComplexData)) return ""; + const size_t index = static_cast(e); + return EnumNamesData()[index]; +} + +template struct DataTraits { + static const Data enum_value = Data::NONE; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::ToggleDataPoint; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::NumberDataPoint; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::TextDataPoint; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::SimpleData; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::CombinedData; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::ListData; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::ComplexData; +}; + +bool VerifyData(::flatbuffers::Verifier &verifier, const void *obj, Data type); +bool VerifyDataVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); + +enum class Schema : uint8_t { + NONE = 0, + ConstantSchema = 1, + SimpleSchema = 2, + CombinedSchema = 3, + ListSchema = 4, + ComplexSchema = 5, + MIN = NONE, + MAX = ComplexSchema +}; + +inline const Schema (&EnumValuesSchema())[6] { + static const Schema values[] = { + Schema::NONE, + Schema::ConstantSchema, + Schema::SimpleSchema, + Schema::CombinedSchema, + Schema::ListSchema, + Schema::ComplexSchema + }; + return values; +} + +inline const char * const *EnumNamesSchema() { + static const char * const names[7] = { + "NONE", + "ConstantSchema", + "SimpleSchema", + "CombinedSchema", + "ListSchema", + "ComplexSchema", + nullptr + }; + return names; +} + +inline const char *EnumNameSchema(Schema e) { + if (::flatbuffers::IsOutRange(e, Schema::NONE, Schema::ComplexSchema)) return ""; + const size_t index = static_cast(e); + return EnumNamesSchema()[index]; +} + +template struct SchemaTraits { + static const Schema enum_value = Schema::NONE; +}; + +template<> struct SchemaTraits { + static const Schema enum_value = Schema::ConstantSchema; +}; + +template<> struct SchemaTraits { + static const Schema enum_value = Schema::SimpleSchema; +}; + +template<> struct SchemaTraits { + static const Schema enum_value = Schema::CombinedSchema; +}; + +template<> struct SchemaTraits { + static const Schema enum_value = Schema::ListSchema; +}; + +template<> struct SchemaTraits { + static const Schema enum_value = Schema::ComplexSchema; +}; + +bool VerifySchema(::flatbuffers::Verifier &verifier, const void *obj, Schema type); +bool VerifySchemaVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); + +struct ToggleDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ToggleDataPointBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4 + }; + const ::flatbuffers::Vector *data() const { + return GetPointer *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.EndTable(); + } +}; + +struct ToggleDataPointBuilder { + typedef ToggleDataPoint Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::Vector> data) { + fbb_.AddOffset(ToggleDataPoint::VT_DATA, data); + } + explicit ToggleDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateToggleDataPoint( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> data = 0) { + ToggleDataPointBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +struct ToggleDataPoint::Traits { + using type = ToggleDataPoint; + static auto constexpr Create = CreateToggleDataPoint; +}; + +inline ::flatbuffers::Offset CreateToggleDataPointDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data = nullptr) { + auto data__ = data ? _fbb.CreateVector(*data) : 0; + return Ikarus::Value::CreateToggleDataPoint( + _fbb, + data__); +} + +struct NumberDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef NumberDataPointBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4 + }; + const ::flatbuffers::Vector *data() const { + return GetPointer *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.EndTable(); + } +}; + +struct NumberDataPointBuilder { + typedef NumberDataPoint Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::Vector> data) { + fbb_.AddOffset(NumberDataPoint::VT_DATA, data); + } + explicit NumberDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateNumberDataPoint( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> data = 0) { + NumberDataPointBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +struct NumberDataPoint::Traits { + using type = NumberDataPoint; + static auto constexpr Create = CreateNumberDataPoint; +}; + +inline ::flatbuffers::Offset CreateNumberDataPointDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data = nullptr) { + auto data__ = data ? _fbb.CreateVector(*data) : 0; + return Ikarus::Value::CreateNumberDataPoint( + _fbb, + data__); +} + +struct TextDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef TextDataPointBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4 + }; + const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *data() const { + return GetPointer> *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.VerifyVectorOfStrings(data()) && + verifier.EndTable(); + } +}; + +struct TextDataPointBuilder { + typedef TextDataPoint Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> data) { + fbb_.AddOffset(TextDataPoint::VT_DATA, data); + } + explicit TextDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateTextDataPoint( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> data = 0) { + TextDataPointBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +struct TextDataPoint::Traits { + using type = TextDataPoint; + static auto constexpr Create = CreateTextDataPoint; +}; + +inline ::flatbuffers::Offset CreateTextDataPointDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector<::flatbuffers::Offset<::flatbuffers::String>> *data = nullptr) { + auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::String>>(*data) : 0; + return Ikarus::Value::CreateTextDataPoint( + _fbb, + data__); +} + +struct ConstantSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ConstantSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SUB_SCHEMA_TYPE = 4, + VT_SUB_SCHEMA = 6, + VT_DATA_TYPE = 8, + VT_DATA = 10 + }; + Ikarus::Value::Schema sub_schema_type() const { + return static_cast(GetField(VT_SUB_SCHEMA_TYPE, 0)); + } + const void *sub_schema() const { + return GetPointer(VT_SUB_SCHEMA); + } + template const T *sub_schema_as() const; + const Ikarus::Value::ConstantSchema *sub_schema_as_ConstantSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *sub_schema_as_SimpleSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *sub_schema_as_CombinedSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::ListSchema *sub_schema_as_ListSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *sub_schema_as_ComplexSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(sub_schema()) : nullptr; + } + Ikarus::Value::Data data_type() const { + return static_cast(GetField(VT_DATA_TYPE, 0)); + } + const void *data() const { + return GetPointer(VT_DATA); + } + template const T *data_as() const; + const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { + return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { + return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { + return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::SimpleData *data_as_SimpleData() const { + return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::CombinedData *data_as_CombinedData() const { + return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ListData *data_as_ListData() const { + return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ComplexData *data_as_ComplexData() const { + return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SUB_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_SUB_SCHEMA) && + VerifySchema(verifier, sub_schema(), sub_schema_type()) && + VerifyField(verifier, VT_DATA_TYPE, 1) && + VerifyOffset(verifier, VT_DATA) && + VerifyData(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ConstantSchema *ConstantSchema::sub_schema_as() const { + return sub_schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *ConstantSchema::sub_schema_as() const { + return sub_schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *ConstantSchema::sub_schema_as() const { + return sub_schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *ConstantSchema::sub_schema_as() const { + return sub_schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *ConstantSchema::sub_schema_as() const { + return sub_schema_as_ComplexSchema(); +} + +template<> inline const Ikarus::Value::ToggleDataPoint *ConstantSchema::data_as() const { + return data_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *ConstantSchema::data_as() const { + return data_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *ConstantSchema::data_as() const { + return data_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *ConstantSchema::data_as() const { + return data_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *ConstantSchema::data_as() const { + return data_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *ConstantSchema::data_as() const { + return data_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *ConstantSchema::data_as() const { + return data_as_ComplexData(); +} + +struct ConstantSchemaBuilder { + typedef ConstantSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_sub_schema_type(Ikarus::Value::Schema sub_schema_type) { + fbb_.AddElement(ConstantSchema::VT_SUB_SCHEMA_TYPE, static_cast(sub_schema_type), 0); + } + void add_sub_schema(::flatbuffers::Offset sub_schema) { + fbb_.AddOffset(ConstantSchema::VT_SUB_SCHEMA, sub_schema); + } + void add_data_type(Ikarus::Value::Data data_type) { + fbb_.AddElement(ConstantSchema::VT_DATA_TYPE, static_cast(data_type), 0); + } + void add_data(::flatbuffers::Offset data) { + fbb_.AddOffset(ConstantSchema::VT_DATA, data); + } + explicit ConstantSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateConstantSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + Ikarus::Value::Schema sub_schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset sub_schema = 0, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset data = 0) { + ConstantSchemaBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_sub_schema(sub_schema); + builder_.add_data_type(data_type); + builder_.add_sub_schema_type(sub_schema_type); + return builder_.Finish(); +} + +struct ConstantSchema::Traits { + using type = ConstantSchema; + static auto constexpr Create = CreateConstantSchema; +}; + +struct SimpleSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef SimpleSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SUB_SCHEMA_TYPE = 4, + VT_SUB_SCHEMA = 6 + }; + Ikarus::Value::Schema sub_schema_type() const { + return static_cast(GetField(VT_SUB_SCHEMA_TYPE, 0)); + } + const void *sub_schema() const { + return GetPointer(VT_SUB_SCHEMA); + } + template const T *sub_schema_as() const; + const Ikarus::Value::ConstantSchema *sub_schema_as_ConstantSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *sub_schema_as_SimpleSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *sub_schema_as_CombinedSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::ListSchema *sub_schema_as_ListSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *sub_schema_as_ComplexSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(sub_schema()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SUB_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_SUB_SCHEMA) && + VerifySchema(verifier, sub_schema(), sub_schema_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ConstantSchema *SimpleSchema::sub_schema_as() const { + return sub_schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *SimpleSchema::sub_schema_as() const { + return sub_schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *SimpleSchema::sub_schema_as() const { + return sub_schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *SimpleSchema::sub_schema_as() const { + return sub_schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *SimpleSchema::sub_schema_as() const { + return sub_schema_as_ComplexSchema(); +} + +struct SimpleSchemaBuilder { + typedef SimpleSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_sub_schema_type(Ikarus::Value::Schema sub_schema_type) { + fbb_.AddElement(SimpleSchema::VT_SUB_SCHEMA_TYPE, static_cast(sub_schema_type), 0); + } + void add_sub_schema(::flatbuffers::Offset sub_schema) { + fbb_.AddOffset(SimpleSchema::VT_SUB_SCHEMA, sub_schema); + } + explicit SimpleSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateSimpleSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + Ikarus::Value::Schema sub_schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset sub_schema = 0) { + SimpleSchemaBuilder builder_(_fbb); + builder_.add_sub_schema(sub_schema); + builder_.add_sub_schema_type(sub_schema_type); + return builder_.Finish(); +} + +struct SimpleSchema::Traits { + using type = SimpleSchema; + static auto constexpr Create = CreateSimpleSchema; +}; + +struct SimpleData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef SimpleDataBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA_TYPE = 4, + VT_DATA = 6 + }; + Ikarus::Value::Data data_type() const { + return static_cast(GetField(VT_DATA_TYPE, 0)); + } + const void *data() const { + return GetPointer(VT_DATA); + } + template const T *data_as() const; + const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { + return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { + return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { + return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::SimpleData *data_as_SimpleData() const { + return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::CombinedData *data_as_CombinedData() const { + return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ListData *data_as_ListData() const { + return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ComplexData *data_as_ComplexData() const { + return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_DATA_TYPE, 1) && + VerifyOffset(verifier, VT_DATA) && + VerifyData(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ToggleDataPoint *SimpleData::data_as() const { + return data_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *SimpleData::data_as() const { + return data_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *SimpleData::data_as() const { + return data_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *SimpleData::data_as() const { + return data_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *SimpleData::data_as() const { + return data_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *SimpleData::data_as() const { + return data_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *SimpleData::data_as() const { + return data_as_ComplexData(); +} + +struct SimpleDataBuilder { + typedef SimpleData Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data_type(Ikarus::Value::Data data_type) { + fbb_.AddElement(SimpleData::VT_DATA_TYPE, static_cast(data_type), 0); + } + void add_data(::flatbuffers::Offset data) { + fbb_.AddOffset(SimpleData::VT_DATA, data); + } + explicit SimpleDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateSimpleData( + ::flatbuffers::FlatBufferBuilder &_fbb, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset data = 0) { + SimpleDataBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_data_type(data_type); + return builder_.Finish(); +} + +struct SimpleData::Traits { + using type = SimpleData; + static auto constexpr Create = CreateSimpleData; +}; + +struct CombinedSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef CombinedSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SCHEMAS_TYPE = 4, + VT_SCHEMAS = 6 + }; + const ::flatbuffers::Vector *schemas_type() const { + return GetPointer *>(VT_SCHEMAS_TYPE); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *schemas() const { + return GetPointer> *>(VT_SCHEMAS); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_SCHEMAS_TYPE) && + verifier.VerifyVector(schemas_type()) && + VerifyOffset(verifier, VT_SCHEMAS) && + verifier.VerifyVector(schemas()) && + VerifySchemaVector(verifier, schemas(), schemas_type()) && + verifier.EndTable(); + } +}; + +struct CombinedSchemaBuilder { + typedef CombinedSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_schemas_type(::flatbuffers::Offset<::flatbuffers::Vector> schemas_type) { + fbb_.AddOffset(CombinedSchema::VT_SCHEMAS_TYPE, schemas_type); + } + void add_schemas(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas) { + fbb_.AddOffset(CombinedSchema::VT_SCHEMAS, schemas); + } + explicit CombinedSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateCombinedSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> schemas_type = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas = 0) { + CombinedSchemaBuilder builder_(_fbb); + builder_.add_schemas(schemas); + builder_.add_schemas_type(schemas_type); + return builder_.Finish(); +} + +struct CombinedSchema::Traits { + using type = CombinedSchema; + static auto constexpr Create = CreateCombinedSchema; +}; + +inline ::flatbuffers::Offset CreateCombinedSchemaDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *schemas_type = nullptr, + const std::vector<::flatbuffers::Offset> *schemas = nullptr) { + auto schemas_type__ = schemas_type ? _fbb.CreateVector(*schemas_type) : 0; + auto schemas__ = schemas ? _fbb.CreateVector<::flatbuffers::Offset>(*schemas) : 0; + return Ikarus::Value::CreateCombinedSchema( + _fbb, + schemas_type__, + schemas__); +} + +struct CombinedData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef CombinedDataBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA_TYPE = 4, + VT_DATA = 6 + }; + const ::flatbuffers::Vector *data_type() const { + return GetPointer *>(VT_DATA_TYPE); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { + return GetPointer> *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA_TYPE) && + verifier.VerifyVector(data_type()) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + VerifyDataVector(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +struct CombinedDataBuilder { + typedef CombinedData Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data_type(::flatbuffers::Offset<::flatbuffers::Vector> data_type) { + fbb_.AddOffset(CombinedData::VT_DATA_TYPE, data_type); + } + void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { + fbb_.AddOffset(CombinedData::VT_DATA, data); + } + explicit CombinedDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateCombinedData( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> data_type = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { + CombinedDataBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_data_type(data_type); + return builder_.Finish(); +} + +struct CombinedData::Traits { + using type = CombinedData; + static auto constexpr Create = CreateCombinedData; +}; + +inline ::flatbuffers::Offset CreateCombinedDataDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data_type = nullptr, + const std::vector<::flatbuffers::Offset> *data = nullptr) { + auto data_type__ = data_type ? _fbb.CreateVector(*data_type) : 0; + auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; + return Ikarus::Value::CreateCombinedData( + _fbb, + data_type__, + data__); +} + +struct ListSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ListSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SCHEMA_TYPE = 4, + VT_SCHEMA = 6 + }; + Ikarus::Value::Schema schema_type() const { + return static_cast(GetField(VT_SCHEMA_TYPE, 0)); + } + const void *schema() const { + return GetPointer(VT_SCHEMA); + } + template const T *schema_as() const; + const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { + return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { + return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { + return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ListSchema *schema_as_ListSchema() const { + return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { + return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_SCHEMA) && + VerifySchema(verifier, schema(), schema_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ConstantSchema *ListSchema::schema_as() const { + return schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *ListSchema::schema_as() const { + return schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *ListSchema::schema_as() const { + return schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *ListSchema::schema_as() const { + return schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *ListSchema::schema_as() const { + return schema_as_ComplexSchema(); +} + +struct ListSchemaBuilder { + typedef ListSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_schema_type(Ikarus::Value::Schema schema_type) { + fbb_.AddElement(ListSchema::VT_SCHEMA_TYPE, static_cast(schema_type), 0); + } + void add_schema(::flatbuffers::Offset schema) { + fbb_.AddOffset(ListSchema::VT_SCHEMA, schema); + } + explicit ListSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateListSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset schema = 0) { + ListSchemaBuilder builder_(_fbb); + builder_.add_schema(schema); + builder_.add_schema_type(schema_type); + return builder_.Finish(); +} + +struct ListSchema::Traits { + using type = ListSchema; + static auto constexpr Create = CreateListSchema; +}; + +struct ListData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ListDataBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA_TYPE = 4, + VT_DATA = 6 + }; + const ::flatbuffers::Vector *data_type() const { + return GetPointer *>(VT_DATA_TYPE); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { + return GetPointer> *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA_TYPE) && + verifier.VerifyVector(data_type()) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + VerifyDataVector(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +struct ListDataBuilder { + typedef ListData Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data_type(::flatbuffers::Offset<::flatbuffers::Vector> data_type) { + fbb_.AddOffset(ListData::VT_DATA_TYPE, data_type); + } + void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { + fbb_.AddOffset(ListData::VT_DATA, data); + } + explicit ListDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateListData( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> data_type = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { + ListDataBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_data_type(data_type); + return builder_.Finish(); +} + +struct ListData::Traits { + using type = ListData; + static auto constexpr Create = CreateListData; +}; + +inline ::flatbuffers::Offset CreateListDataDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data_type = nullptr, + const std::vector<::flatbuffers::Offset> *data = nullptr) { + auto data_type__ = data_type ? _fbb.CreateVector(*data_type) : 0; + auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; + return Ikarus::Value::CreateListData( + _fbb, + data_type__, + data__); +} + +struct NamedSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef NamedSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_NAME = 4, + VT_SCHEMA_TYPE = 6, + VT_SCHEMA = 8 + }; + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + Ikarus::Value::Schema schema_type() const { + return static_cast(GetField(VT_SCHEMA_TYPE, 0)); + } + const void *schema() const { + return GetPointer(VT_SCHEMA); + } + template const T *schema_as() const; + const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { + return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { + return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { + return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ListSchema *schema_as_ListSchema() const { + return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { + return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_NAME) && + verifier.VerifyString(name()) && + VerifyField(verifier, VT_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_SCHEMA) && + VerifySchema(verifier, schema(), schema_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ConstantSchema *NamedSchema::schema_as() const { + return schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *NamedSchema::schema_as() const { + return schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *NamedSchema::schema_as() const { + return schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *NamedSchema::schema_as() const { + return schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *NamedSchema::schema_as() const { + return schema_as_ComplexSchema(); +} + +struct NamedSchemaBuilder { + typedef NamedSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { + fbb_.AddOffset(NamedSchema::VT_NAME, name); + } + void add_schema_type(Ikarus::Value::Schema schema_type) { + fbb_.AddElement(NamedSchema::VT_SCHEMA_TYPE, static_cast(schema_type), 0); + } + void add_schema(::flatbuffers::Offset schema) { + fbb_.AddOffset(NamedSchema::VT_SCHEMA, schema); + } + explicit NamedSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateNamedSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, + Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset schema = 0) { + NamedSchemaBuilder builder_(_fbb); + builder_.add_schema(schema); + builder_.add_name(name); + builder_.add_schema_type(schema_type); + return builder_.Finish(); +} + +struct NamedSchema::Traits { + using type = NamedSchema; + static auto constexpr Create = CreateNamedSchema; +}; + +inline ::flatbuffers::Offset CreateNamedSchemaDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset schema = 0) { + auto name__ = name ? _fbb.CreateString(name) : 0; + return Ikarus::Value::CreateNamedSchema( + _fbb, + name__, + schema_type, + schema); +} + +struct ComplexSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ComplexSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SCHEMAS = 4 + }; + const ::flatbuffers::Vector<::flatbuffers::Offset> *schemas() const { + return GetPointer> *>(VT_SCHEMAS); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_SCHEMAS) && + verifier.VerifyVector(schemas()) && + verifier.VerifyVectorOfTables(schemas()) && + verifier.EndTable(); + } +}; + +struct ComplexSchemaBuilder { + typedef ComplexSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_schemas(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas) { + fbb_.AddOffset(ComplexSchema::VT_SCHEMAS, schemas); + } + explicit ComplexSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateComplexSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas = 0) { + ComplexSchemaBuilder builder_(_fbb); + builder_.add_schemas(schemas); + return builder_.Finish(); +} + +struct ComplexSchema::Traits { + using type = ComplexSchema; + static auto constexpr Create = CreateComplexSchema; +}; + +inline ::flatbuffers::Offset CreateComplexSchemaDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector<::flatbuffers::Offset> *schemas = nullptr) { + auto schemas__ = schemas ? _fbb.CreateVector<::flatbuffers::Offset>(*schemas) : 0; + return Ikarus::Value::CreateComplexSchema( + _fbb, + schemas__); +} + +struct NamedData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef NamedDataBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_NAME = 4, + VT_DATA_TYPE = 6, + VT_DATA = 8 + }; + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + Ikarus::Value::Data data_type() const { + return static_cast(GetField(VT_DATA_TYPE, 0)); + } + const void *data() const { + return GetPointer(VT_DATA); + } + template const T *data_as() const; + const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { + return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { + return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { + return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::SimpleData *data_as_SimpleData() const { + return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::CombinedData *data_as_CombinedData() const { + return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ListData *data_as_ListData() const { + return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ComplexData *data_as_ComplexData() const { + return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_NAME) && + verifier.VerifyString(name()) && + VerifyField(verifier, VT_DATA_TYPE, 1) && + VerifyOffset(verifier, VT_DATA) && + VerifyData(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ToggleDataPoint *NamedData::data_as() const { + return data_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *NamedData::data_as() const { + return data_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *NamedData::data_as() const { + return data_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *NamedData::data_as() const { + return data_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *NamedData::data_as() const { + return data_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *NamedData::data_as() const { + return data_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *NamedData::data_as() const { + return data_as_ComplexData(); +} + +struct NamedDataBuilder { + typedef NamedData Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { + fbb_.AddOffset(NamedData::VT_NAME, name); + } + void add_data_type(Ikarus::Value::Data data_type) { + fbb_.AddElement(NamedData::VT_DATA_TYPE, static_cast(data_type), 0); + } + void add_data(::flatbuffers::Offset data) { + fbb_.AddOffset(NamedData::VT_DATA, data); + } + explicit NamedDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateNamedData( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset data = 0) { + NamedDataBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_name(name); + builder_.add_data_type(data_type); + return builder_.Finish(); +} + +struct NamedData::Traits { + using type = NamedData; + static auto constexpr Create = CreateNamedData; +}; + +inline ::flatbuffers::Offset CreateNamedDataDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset data = 0) { + auto name__ = name ? _fbb.CreateString(name) : 0; + return Ikarus::Value::CreateNamedData( + _fbb, + name__, + data_type, + data); +} + +struct ComplexData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ComplexDataBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4 + }; + const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { + return GetPointer> *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.VerifyVectorOfTables(data()) && + verifier.EndTable(); + } +}; + +struct ComplexDataBuilder { + typedef ComplexData Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { + fbb_.AddOffset(ComplexData::VT_DATA, data); + } + explicit ComplexDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateComplexData( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { + ComplexDataBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +struct ComplexData::Traits { + using type = ComplexData; + static auto constexpr Create = CreateComplexData; +}; + +inline ::flatbuffers::Offset CreateComplexDataDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector<::flatbuffers::Offset> *data = nullptr) { + auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; + return Ikarus::Value::CreateComplexData( + _fbb, + data__); +} + +struct Value FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ValueBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SCHEMA_TYPE = 4, + VT_SCHEMA = 6, + VT_DATA_TYPE = 8, + VT_DATA = 10 + }; + Ikarus::Value::Schema schema_type() const { + return static_cast(GetField(VT_SCHEMA_TYPE, 0)); + } + const void *schema() const { + return GetPointer(VT_SCHEMA); + } + template const T *schema_as() const; + const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { + return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { + return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { + return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ListSchema *schema_as_ListSchema() const { + return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { + return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; + } + Ikarus::Value::Data data_type() const { + return static_cast(GetField(VT_DATA_TYPE, 0)); + } + const void *data() const { + return GetPointer(VT_DATA); + } + template const T *data_as() const; + const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { + return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { + return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { + return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::SimpleData *data_as_SimpleData() const { + return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::CombinedData *data_as_CombinedData() const { + return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ListData *data_as_ListData() const { + return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ComplexData *data_as_ComplexData() const { + return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_SCHEMA) && + VerifySchema(verifier, schema(), schema_type()) && + VerifyField(verifier, VT_DATA_TYPE, 1) && + VerifyOffset(verifier, VT_DATA) && + VerifyData(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ConstantSchema *Value::schema_as() const { + return schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *Value::schema_as() const { + return schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *Value::schema_as() const { + return schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *Value::schema_as() const { + return schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *Value::schema_as() const { + return schema_as_ComplexSchema(); +} + +template<> inline const Ikarus::Value::ToggleDataPoint *Value::data_as() const { + return data_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *Value::data_as() const { + return data_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *Value::data_as() const { + return data_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *Value::data_as() const { + return data_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *Value::data_as() const { + return data_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *Value::data_as() const { + return data_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *Value::data_as() const { + return data_as_ComplexData(); +} + +struct ValueBuilder { + typedef Value Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_schema_type(Ikarus::Value::Schema schema_type) { + fbb_.AddElement(Value::VT_SCHEMA_TYPE, static_cast(schema_type), 0); + } + void add_schema(::flatbuffers::Offset schema) { + fbb_.AddOffset(Value::VT_SCHEMA, schema); + } + void add_data_type(Ikarus::Value::Data data_type) { + fbb_.AddElement(Value::VT_DATA_TYPE, static_cast(data_type), 0); + } + void add_data(::flatbuffers::Offset data) { + fbb_.AddOffset(Value::VT_DATA, data); + } + explicit ValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateValue( + ::flatbuffers::FlatBufferBuilder &_fbb, + Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset schema = 0, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset data = 0) { + ValueBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_schema(schema); + builder_.add_data_type(data_type); + builder_.add_schema_type(schema_type); + return builder_.Finish(); +} + +struct Value::Traits { + using type = Value; + static auto constexpr Create = CreateValue; +}; + +inline bool VerifyData(::flatbuffers::Verifier &verifier, const void *obj, Data type) { + switch (type) { + case Data::NONE: { + return true; + } + case Data::ToggleDataPoint: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::NumberDataPoint: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::TextDataPoint: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::SimpleData: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::CombinedData: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::ListData: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::ComplexData: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + default: return true; + } +} + +inline bool VerifyDataVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { + if (!values || !types) return !values && !types; + if (values->size() != types->size()) return false; + for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { + if (!VerifyData( + verifier, values->Get(i), types->GetEnum(i))) { + return false; + } + } + return true; +} + +inline bool VerifySchema(::flatbuffers::Verifier &verifier, const void *obj, Schema type) { + switch (type) { + case Schema::NONE: { + return true; + } + case Schema::ConstantSchema: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Schema::SimpleSchema: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Schema::CombinedSchema: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Schema::ListSchema: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Schema::ComplexSchema: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + default: return true; + } +} + +inline bool VerifySchemaVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { + if (!values || !types) return !values && !types; + if (values->size() != types->size()) return false; + for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { + if (!VerifySchema( + verifier, values->Get(i), types->GetEnum(i))) { + return false; + } + } + return true; +} + +inline const Ikarus::Value::Value *GetValue(const void *buf) { + return ::flatbuffers::GetRoot(buf); +} + +inline const Ikarus::Value::Value *GetSizePrefixedValue(const void *buf) { + return ::flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyValueBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedValueBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishValueBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedValueBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace Value +} // namespace Ikarus + +#endif // FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_H_ diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index ec2087f..9ad444e 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -4,7 +4,6 @@ /// \author Folling #include -#include #include #include @@ -14,9 +13,10 @@ IKARUS_BEGIN_HEADER -/// \brief A blueprint object. -/// \details A blueprint is a collection of properties which can be linked to entities. -/// Each entity the blueprint is linked to will have values for the blueprints properties.q +/// \brief Blueprints are templates for managing common properties of entities. +/// \details A blueprint is a collection of properties which can be linked to entities. +/// Each entity the blueprint is linked to will have values for the blueprints +/// properties. Changes in blueprints will be reflected in all linked entities. struct IkarusBlueprint; /// \brief Creates a blueprint. @@ -25,19 +25,24 @@ struct IkarusBlueprint; /// \pre \li Must exist. /// \param name The name of the blueprint. /// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \pre \li Must be unique among all blueprints in the project. /// \param error_out \see errors.h /// \return The created blueprint or null if an error occurs. -IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); +IKA_API IkarusBlueprint * ikarus_blueprint_create( + struct IkarusProject * project, + char const * name, + IkarusErrorData * error_out +); -/// \brief Deletes & frees a blueprint. +/// \brief Deletes a blueprint. /// \param blueprint The blueprint to delete. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h /// \remark The blueprint must not be accessed after deletion. -IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * error_out); +IKA_API void ikarus_blueprint_delete( + IkarusBlueprint * blueprint, + IkarusErrorData * error_out +); /// \brief Gets the ID of a blueprint. /// \param blueprint The blueprint to get the ID of. @@ -45,23 +50,32 @@ IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorDat /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The ID of the blueprint or 0 if an error occurs. -IKA_API IkarusId ikarus_blueprint_get_id(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); +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. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The project the blueprint is part of or null if an error occurs. -IKA_API IkarusProject * ikarus_blueprint_get_project(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); +/// \return The project or null if an error occurs. +IKA_API IkarusProject * ikarus_blueprint_get_project( + IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +); /// \brief Gets the name of a blueprint. /// \param blueprint The blueprint 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 blueprint or null if an error occurs. -IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); +/// \return The name or null if an error occurs. +IKA_API char const * ikarus_blueprint_get_name( + IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +); /// \brief Sets the name of a blueprint. /// \param blueprint The blueprint to set the name of. @@ -69,10 +83,12 @@ IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint /// \pre \li Must exist. /// \param name The new name of the blueprint. /// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \pre \li Must be unique among all blueprints in the project. /// \param error_out \see errors.h -IKA_API void ikarus_blueprint_set_name(IkarusBlueprint * blueprint, char const * name, IkarusErrorData * error_out); +IKA_API void ikarus_blueprint_set_name( + IkarusBlueprint * blueprint, + char const * name, + IkarusErrorData * error_out +); /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. @@ -96,7 +112,10 @@ IKA_API void ikarus_blueprint_get_properties( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); +IKA_API size_t ikarus_blueprint_get_property_count( + IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +); /// \brief Gets the entities linked to a blueprint. /// \param blueprint The blueprint to get the linked entities of. @@ -120,7 +139,10 @@ IKA_API void ikarus_blueprint_get_linked_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of linked entities or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); +IKA_API size_t ikarus_blueprint_get_linked_entity_count( + IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 04d59b0..28cebf8 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -14,25 +13,20 @@ IKARUS_BEGIN_HEADER /// \brief Entities are the core building blocks of Ikarus. -/// \detials Blueprints and Properties define the structure of the data. -/// Entities define the data itself. +/// \details Entities store two data: A name and a set of values. +/// The name identifies the entity but does not have to be unique. The values +/// are the actual information of the entity. /// -/// Properties can be associated with Entities in two ways: -/// - Directly: The property is linked to the entity. -/// - Indirectly: The property is linked to a blueprint the entity is linked to. +/// For documentation on the types and layout of values \see Values.h. /// -/// For each property an entity is linked to, it has a value. These values depend on the property's type. -/// For more information on the types see the property documentation. +/// Entities can be linked to blueprints. +/// The entity has a (possibly uninitialized) value for each property of all +/// blueprints it is linked to. \see Property.h \see Blueprint.h. /// -/// Values are the core type of data within Ikarus. -/// Each value is associated with one page and one property. -/// -/// \remark Values are typed, the type of a value is specified by its associated property. -/// For more information on the types see the property documentation. -/// -/// \remark Values are guaranteed to be in valid format for a given type -/// but not guaranteed to be valid under the settings of the property. -/// This is because changing the settings can invalidate existing values without resetting them. +/// We distinguish between `EntityValues` and `EntityPropertyValues`. +/// `EntityValues` are a direct part of the entity. +/// `EntityPropertyValues` are an indirect part of the entity, linked to them +/// via a blueprint. struct IkarusEntity; /// \brief Creates an entity. @@ -41,11 +35,13 @@ struct IkarusEntity; /// \pre \li Must exist. /// \param name The name of the entity. /// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \pre \li Must be unique among all entities in the project. /// \param error_out \see errors.h /// \return The created entity or null if an error occurs. -IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); +IKA_API IkarusEntity * ikarus_entity_create( + struct IkarusProject * project, + char const * name, + IkarusErrorData * error_out +); /// \brief Deletes an entity. /// \param entity The entity to delete. @@ -53,15 +49,17 @@ IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char /// \pre \li Must exist. /// \param error_out \see errors.h /// \remark The entity must not be accessed after deletion. -IKA_API void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); +IKA_API void +ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); -/// \brief Gets the ID of an entity. -/// \param entity The entity to get the ID of. +/// \brief Gets the id of an entity. +/// \param entity The entity 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 entity or 0 if an error occurs. -IKA_API IkarusId ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out); +/// \return The id of the entity or 0 if an error occurs. +IKA_API int64_t +ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out); /// \brief Gets the project an entity is part of. /// \param entity The entity to get the project of. @@ -69,7 +67,10 @@ IKA_API IkarusId ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorDa /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The project the entity is part of or null if an error occurs. -IKA_API IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity, IkarusErrorData * error_out); +IKA_API IkarusProject * ikarus_entity_get_project( + IkarusEntity const * entity, + IkarusErrorData * error_out +); /// \brief Gets the name of an entity. /// \param entity The entity to get the name of. @@ -77,7 +78,10 @@ IKA_API IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity, I /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The name of the entity or null if an error occurs. -IKA_API char const * ikarus_entity_get_name(IkarusEntity const * entity, IkarusErrorData * error_out); +IKA_API char const * ikarus_entity_get_name( + IkarusEntity const * entity, + IkarusErrorData * error_out +); /// \brief Sets the name of an entity. /// \param entity The entity to set the name of. @@ -85,10 +89,12 @@ IKA_API char const * ikarus_entity_get_name(IkarusEntity const * entity, IkarusE /// \pre \li Must exist. /// \param name The new name of the entity. /// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \pre \li Must be unique among all entities in the project. /// \param error_out \see errors.h -IKA_API void ikarus_entity_set_name(IkarusEntity * entity, char const * name, IkarusErrorData * error_out); +IKA_API void ikarus_entity_set_name( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out +); /// \brief Checks if an entity is linked to a blueprint. /// \param entity The entity to check. @@ -99,8 +105,11 @@ IKA_API void ikarus_entity_set_name(IkarusEntity * entity, char const * name, Ik /// \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); +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. @@ -111,10 +120,14 @@ ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusB /// \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); +IKA_API void ikarus_entity_link_to_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusErrorData * error_out +); -/// \brief Unlinks an entity from a blueprint. All values of the properties of the blueprint the entity is linked with -/// will be deleted. +/// \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. @@ -123,9 +136,13 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru /// \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); +IKA_API void ikarus_entity_unlink_from_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusErrorData * error_out +); -/// \brief Gets the blueprints an entity is linked to. +/// \brief Gets all blueprints an entity is linked to. /// \param entity The entity to get the blueprints of. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -142,14 +159,80 @@ IKA_API void ikarus_entity_get_linked_blueprints( ); /// \brief Gets the number of blueprints an entity is linked to. -/// \param entity The entity to get the number of blueprints of. +/// \param entity The entity to get the number of linked blueprints of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_entity_get_linked_blueprint_count(IkarusEntity const * entity, IkarusErrorData * error_out); +IKA_API size_t ikarus_entity_get_linked_blueprint_count( + IkarusEntity const * entity, + IkarusErrorData * error_out +); -/// \brief Checks if an entity has a specific property. +/// \brief Checks if an entity has a value with a given name. +/// \param entity The entity to check. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the value to check. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return False if the entity does not have a value associated with +/// the name or if an error occurs, true otherwise. +IKA_API bool ikarus_entity_has_value( + IkarusEntity const * entity, + char const * name, + IkarusErrorData * error_out +); + +/// \brief Gets the value of an entity. +/// \param entity The entity to get the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the value to get. +/// \pre \li Must not be null. +/// \pre \li Must be an existing value of the entity. +/// \param error_out \see errors.h +/// \return The value of the entity or null if an error occurs. +IKA_API struct IkarusValue * ikarus_entity_get_value( + IkarusEntity const * entity, + char const * name, + IkarusErrorData * error_out +); + +/// \brief Sets the value of an entity. +/// If the entity does not have a value associated with the name, it is created. +/// \remark Types are overwritten if the value already exists. +/// \param entity The entity to set the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the value to set. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param value The new value of the entity. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +IKA_API void ikarus_entity_set_value( + IkarusEntity * entity, + char const * name, + struct IkarusValue const * value, + IkarusErrorData * error_out +); + +/// \brief Removes a value from an entity. +/// \pre \li The value must exist. +/// \param entity The entity to delete the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the value to delete. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +IKA_API void ikarus_entity_remove_value( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out +); + +/// \brief Checks if a property is linked to an entity. /// \param entity The entity to check. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -157,14 +240,19 @@ IKA_API size_t ikarus_entity_get_linked_blueprint_count(IkarusEntity const * ent /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return False if an error occurs or the entity does not have the property, true otherwise. -IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); +/// \return False if an error occurs or the entity does not have the property, +/// true otherwise. +IKA_API bool ikarus_entity_has_property( + IkarusEntity const * entity, + struct IkarusProperty const * property, + IkarusErrorData * error_out +); -/// \brief Gets the properties of an entity. -/// \param entity The entity to get the properties of. +/// \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 properties to. +/// \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. @@ -176,30 +264,37 @@ IKA_API void ikarus_entity_get_properties( IkarusErrorData * error_out ); -/// \brief Gets the number of properties of an entity. -/// \param entity The entity to get the number of properties of. +/// \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); +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. -/// \details If the entity has never set the value of the property, the default value is returned (which may be undefined). +/// \details If the entity has never set the value of the property, +/// the property's default value is returned. /// \param entity The entity to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param property The property to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \pre \li Must be linked to the entity. /// \param error_out \see errors.h -/// \return The value of the property or null if the entity does not have the property or an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusEntityPropertyValue * -ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); +/// \return The value of the property or null if an error occurs. +IKA_API struct IkarusValue * ikarus_entity_get_property_value( + IkarusEntity const * entity, + struct IkarusProperty const * property, + IkarusErrorData * error_out +); /// \brief Sets the value of a property of an entity. -/// \param entity The entity to set the value of. +/// \param entity The entity to set the property value of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param property The property to set the value of. @@ -211,7 +306,7 @@ ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const /// \pre \li Must be valid for the property's settings. /// \param error_out \see errors.h /// \remark If the entity does not have the property, this function fails. -IKA_API void ikarus_entity_set_value( +IKA_API void ikarus_entity_set_property_value( IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value, diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h deleted file mode 100644 index 6a46e17..0000000 --- a/include/ikarus/objects/object.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -/// \file object.h -/// \author Folling - -#include -#include - -/// \defgroup object Objects -/// \brief Objects are a compound type of all types of objects in the database. -/// \details The following objects currently exist: -/// - \ref blueprint.h "Blueprints" -/// - \ref property.h "Properties" -/// - \ref entity.h "Entities" -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief A generic object. Wraps all types of objects, including folders. -struct IkarusObject; - -/// \brief Visits an object. Calling the appropriate function for the object's type. -/// \param object The object to visit. -/// \param blueprint_visitor The function to call if the object is a blueprint. Skipped if null. -/// \param property_visitor The function to call if the object is a property. Skipped if null. -/// \param entity_visitor The function to call if the object is an entity. Skipped if null. -/// \param data The data passed to the visitor functions. -/// \param error_out \see errors.h -IKA_API void ikarus_object_visit( - IkarusObject * object, - void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *), - void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *), - void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *), - void * data, - IkarusErrorData * error_out -); - -/// \see ikarus_object_visit -IKA_API void ikarus_object_visit_const( - IkarusObject const * object, - void (*blueprint_visitor)(struct IkarusBlueprint const *, IkarusErrorData * error_out, void *), - void (*property_visitor)(struct IkarusProperty const *, IkarusErrorData * error_out, void *), - void (*entity_visitor)(struct IkarusEntity const *, IkarusErrorData * error_out, void *), - void * data, - IkarusErrorData * error_out -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h deleted file mode 100644 index e30bb3c..0000000 --- a/include/ikarus/objects/object_type.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -/// \file object_type.h -/// \author Folling - -#include -#include - -/// \addtogroup objects Objects -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The type of an object. -enum IkarusObjectType { - /// \brief Not an object or no object. - IkarusObjectType_None = 0, - /// \brief An IkarusEntity. - IkarusObjectType_Entity = 0b00000001, - /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 0b00000010, - /// \brief An IkarusProperty. - IkarusObjectType_Property = 0b00000011, -}; - -/// \brief Converts an IkarusObjectType to a string. -/// \param type The type to convert. -/// \return The string representation of the type. -/// \remark The returned string must not be freed. -char const * ikarus_object_type_to_string(IkarusObjectType type); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 2fcf63b..d989ec2 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -20,7 +20,6 @@ struct IkarusNumberProperty; /// \pre \li Must exist. /// \param name The name of the property. /// \pre \li Must not be null. -/// \pre \li Must not be empty. /// \param property_source The property source to create the property for. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -36,31 +35,6 @@ IKA_API IkarusNumberProperty * ikarus_number_property_create( IkarusErrorData * error_out ); -/// \brief Sets the default value for a number property. -/// \param property The number property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The default value or null if an error occurs. -IKA_API struct IkarusNumberValue * -ikarus_number_property_get_default_value(struct IkarusNumberProperty * property, IkarusErrorData * error_out); - -/// \brief Sets the default value for a number property. -/// \param property The number property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \param error_out \see errors.h -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between -/// default values and other settings. -IKA_API void ikarus_number_property_set_default_value( - struct IkarusNumberProperty * property, - struct IkarusNumberValue * new_default_value, - IkarusErrorData * error_out -); - IKARUS_END_HEADER /// @} diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index aa52b2c..c7ba19b 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -4,10 +4,10 @@ /// \author Folling #include -#include #include -#include #include +#include +#include /// \defgroup properties Properties /// \brief Properties define the structure and types of data. @@ -15,9 +15,10 @@ IKARUS_BEGIN_HEADER -/// \brief Properties are the placeholders of values for entities. -/// \details Each entity can have any number of properties. +/// \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 @@ -29,32 +30,10 @@ IKARUS_BEGIN_HEADER /// - Age (Number) /// - ISBN (Text) /// -/// Every property has settings which can be used to customise the property further. -/// Two settings that are shared among all properties are the following: -/// - List -/// - May be undefined -/// -/// Additionally, each property has a default value. If no default value is provided, a sensible default is chosen. -/// Setting a default value that isn't valid for the property is an error. Changing settings so that the current default -/// value becomes invalid is valid but unsets the custom default value. -/// -/// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. -/// The latter allows you to specify an "unknown" value for a property. -/// It might not be known if a character is dead or not for example. -/// /// Each entity associated with the property has a value for it. /// -/// Properties can also be added to blueprints in which case they are available for all entities associated with the -/// blueprint. -/// -/// We call properties within entities "Entity Properties" and properties within blueprints "Blueprint Properties". -/// -/// -/// \remark Properties are scoped to the blueprint or entity they are associated with. -/// \remark Values for properties are lazily created as space saving measure. +/// \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. -/// This default value is specified when the property is created and can be updated later. -/// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. struct IkarusProperty; /// \brief Deletes a property. @@ -71,7 +50,7 @@ IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The ID of the property or 0 if an error occurs. -IKA_API IkarusId ikarus_property_get_id(IkarusProperty const * property, IkarusErrorData * error_out); +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. @@ -81,31 +60,66 @@ IKA_API IkarusId ikarus_property_get_id(IkarusProperty const * property, IkarusE /// \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. -IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out); +/// \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); -/// \brief Gets the source of a property. +// 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. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyScope const * ikarus_property_get_scope(IkarusProperty const * property, IkarusErrorData * error_out); - -/// \brief Gets the default value of a property. -/// \param property The property to get the type info of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The default value of the property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out); +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. @@ -125,28 +139,6 @@ IKA_API void ikarus_property_visit( IkarusErrorData * error_out ); -/// \see ikarus_property_visit -IKA_API 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 -); - -/// \brief Casts a property to an object. -/// \param property The property to cast. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The property represented as an object or null if an error occurs. -/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty * property, IkarusErrorData * error_out); - -/// \see ikarus_property_to_object -IKA_API struct IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property, IkarusErrorData * error_out); - IKARUS_END_HEADER // @} diff --git a/include/ikarus/objects/properties/property_scope.h b/include/ikarus/objects/properties/property_scope.h deleted file mode 100644 index a211e67..0000000 --- a/include/ikarus/objects/properties/property_scope.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -/// \file property_source.h -/// \author Folling - -#include -#include - -/// \addtogroup properties Properties -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusPropertyScope; - -/// \brief Creates an blueprint property source. -/// \param blueprint The blueprint to create the property source for. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The created property source or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyScope * -ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint, IkarusErrorData * error_out); - -/// \brief Creates an entity property source. -/// \param entity The entity to create the property source for. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The created property source or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyScope * ikarus_property_source_create_entity(struct IkarusEntity * entity, IkarusErrorData * error_out); - -/// \brief Visits a property source, calling the appropriate callback. -/// \param property_source The property source to visit. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param blueprint_visitor The callback to call if the source is a blueprint, skipped if null. -/// \param entity_visitor The callback to call if the source is an entity, skipped if null. -/// \param user_data User data to pass to the callbacks. -/// \param error_out \see errors.h -IKA_API void ikarus_property_source_visit( - struct IkarusPropertyScope * property_source, - void (*blueprint_visitor)(struct IkarusBlueprint *, void *), - void (*entity_visitor)(struct IkarusEntity *, void *), - void * user_data, - IkarusErrorData * error_out -); - -/// \see ikarus_property_source_visit -IKA_API void ikarus_property_source_visit_const( - struct IkarusPropertyScope const * property_source, - void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), - void (*entity_visitor)(struct IkarusEntity const *, void *), - void * user_data, - IkarusErrorData * error_out -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/property_type.h b/include/ikarus/objects/properties/property_type.h deleted file mode 100644 index 893cdca..0000000 --- a/include/ikarus/objects/properties/property_type.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -/// \file property_type.h -/// \author Folling - -#include - -/// \addtogroup properties Properties -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The type of a property. -/// \details Designates the type of data stored by the property as well as which settings are -/// available. -enum IkarusPropertyType { - /// \brief A true/false boolean-esque value. - IkarusPropertyType_Toggle = 0x10000000, - /// \brief A numeric value, limited to IEEE 80 bit floating point numbers. - IkarusPropertyType_Number = 0x20000000, - /// \brief An arbitrary UTF-8 textual value. - IkarusPropertyType_Text = 0x30000000, -}; - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index 5e59d92..a0c0a9d 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -20,7 +20,6 @@ struct IkarusTextProperty; /// \pre \li Must exist. /// \param name The name of the property. /// \pre \li Must not be null. -/// \pre \li Must not be empty. /// \param property_source The property source to create the property for. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -32,31 +31,7 @@ IKA_API IkarusTextProperty * ikarus_text_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, - struct IkarusTextValue* default_value, - IkarusErrorData * error_out -); - -/// \brief Sets the default value for a text property. -/// \param property The text property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The default value or null if an error occurs. -IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property, IkarusErrorData * error_out); - -/// \brief Sets the default value for a text property. -/// \param property The text property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \param error_out \see errors.h -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between -/// default values and other settings. -IKA_API void ikarus_text_property_set_default_value( - struct IkarusTextProperty * property, - struct IkarusTextValue * new_default_value, + struct IkarusTextValue * default_value, IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index 403f392..bfec311 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -7,11 +7,11 @@ #include /// \addtogroup properties Properties -/// \brief Toggle properties store a value that can be either true or false. (e.g. "Is the character dead?") /// @{ 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. @@ -20,44 +20,15 @@ struct IkarusToggleProperty; /// \pre \li Must exist. /// \param name The name of the property. /// \pre \li Must not be null. -/// \pre \li Must not be empty. /// \param property_source The property source to create the property for. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param 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 IkarusToggleProperty * ikarus_toggle_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, - struct IkarusToggleValue * default_value, - IkarusErrorData * error_out -); - -/// \brief Sets the default value for a toggle property. -/// \param property The toggle property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The default value or null if an error occurs. -IKA_API struct IkarusToggleValue * -ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, IkarusErrorData * error_out); - -/// \brief Sets the default value for a toggle property. -/// \param property The toggle property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \param error_out \see errors.h -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between -/// default values and other settings. -IKA_API void ikarus_toggle_property_set_default_value( - struct IkarusToggleProperty * property, - struct IkarusToggleValue * new_default_value, IkarusErrorData * error_out ); diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 18a35f5..74258a9 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -30,7 +30,11 @@ struct IkarusProject; /// \return The created project or null if an error occurs. /// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_create(char const * path, char const * name, IkarusErrorData * error_out); +IKA_API IkarusProject * ikarus_project_create( + char const * path, + char const * name, + IkarusErrorData * error_out +); /// \brief Creates a project in memory. /// \param name The name of the project. Must neither be null nor empty. @@ -40,7 +44,8 @@ IKA_API IkarusProject * ikarus_project_create(char const * path, char const * na /// \return The created project or null if an error occurs. /// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out); +IKA_API IkarusProject * +ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out); /// \brief Opens an existing project. /// \param path The path to the project. @@ -50,7 +55,8 @@ IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name, Ikaru /// \return The opened project or null if an error occurs. /// \remark Must be freed using ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_out); +IKA_API IkarusProject * +ikarus_project_open(char const * path, IkarusErrorData * error_out); /// \brief Gets the name of a project. /// \param project The project to get the name of. @@ -59,7 +65,10 @@ IKA_API IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * /// \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); +IKA_API char const * ikarus_project_get_name( + IkarusProject const * project, + IkarusErrorData * error_out +); /// \brief Sets the name of a project. /// \param project The project to set the name of. @@ -69,7 +78,11 @@ IKA_API char const * ikarus_project_get_name(IkarusProject const * project, Ikar /// \pre \li Must not be null. /// \pre \li Must not be empty. /// \param error_out \see errors.h -IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_name, IkarusErrorData * error_out); +IKA_API void ikarus_project_set_name( + IkarusProject * project, + char const * new_name, + IkarusErrorData * error_out +); /// \brief Gets the path of a project. /// \param project The project to get the path of. @@ -78,7 +91,10 @@ IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_n /// \param error_out \see errors.h /// \return The path of the project. /// \remark Ownership remains with libikarus, must not be freed. -IKA_API char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out); +IKA_API char const * ikarus_project_get_path( + IkarusProject const * project, + IkarusErrorData * error_out +); /// \brief Gets the entities of a project. /// \param project The project to get the entities of. @@ -101,7 +117,10 @@ IKA_API void ikarus_project_get_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of entities or undefined if an error occurs. -IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_entity_count( + IkarusProject const * project, + IkarusErrorData * error_out +); /// \brief Gets the blueprints of a project. /// \param project The project to get the blueprints of. @@ -124,51 +143,11 @@ IKA_API void ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); - -/// \brief Finds an entity by a given name. -/// \param project The project to search. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param name The name to search for. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param error_out \see errors.h -/// \return The entity with the given name or null if none was found. -IKA_API struct IkarusEntity * ikarus_project_get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); - -/// \brief Finds a property by a given name. -/// \param project The project to search. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param scope The scope of the property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \remark Property names are unique only within their scope. -/// \param name The name to search for. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param error_out \see errors.h -/// \return The property with the given name or null if none was found. -IKA_API struct IkarusProperty * ikarus_project_get_property_by_name_and_scope( - IkarusProject * project, - struct IkarusPropertyScope * scope, - char const * name, +IKA_API size_t ikarus_project_get_blueprint_count( + IkarusProject const * project, IkarusErrorData * error_out ); -/// \brief Finds a blueprint by a given name. -/// \param project The project to search. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param name The name to search for. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param error_out \see errors.h -/// \return The blueprint with the given name or null if none was found. -IKA_API struct IkarusBlueprint * -ikarus_project_get_blueprint_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); - IKARUS_END_HEADER /// @} diff --git a/include/ikarus/values/entity_property_value.h b/include/ikarus/values/entity_property_value.h deleted file mode 100644 index c408f00..0000000 --- a/include/ikarus/values/entity_property_value.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -/// \file entity_property_value.h -/// \author Folling - -/// \defgroup entity_property_values EntityPropertyValue -/// \brief Values in relation to an entity and one of its properties. -/// @{ - -#include -#include -#include - -IKARUS_BEGIN_HEADER - -/// \brief Like an \ref value.h "IkarusValue", but in relation to an entity and one of its properties -struct IkarusEntityPropertyValue; - -/// \brief Fetches the entity of an entity property value. -/// \param value The entity property value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return The entity of the entity property value. -/// \remark This value is owned by the entity property value and must not be freed directly. -struct IkarusEntity const * ikarus_entity_property_value_get_entity(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); - -/// \brief Fetches the property of an entity property value. -/// \param value The entity property value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return The property of the entity property value. -/// \remark This value is owned by the entity property value and must not be freed directly. -struct IkarusProperty const * -ikarus_entity_property_value_get_property(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); - -/// \brief Fetches the value of an entity property value. -/// \param value The entity property value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return The value of the entity property value. -/// \remark This value is owned by the entity property value and must not be freed directly. -struct IkarusValue const * ikarus_entity_property_value_get_value(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index 1425fed..220a11c 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -5,6 +5,7 @@ #include #include +#include /// \addtogroup values Values /// @{ @@ -15,10 +16,11 @@ IKARUS_BEGIN_HEADER 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. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create(IkarusErrorData * error_out); +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. @@ -26,14 +28,14 @@ IKA_API IkarusNumberValue * ikarus_number_value_create(IkarusErrorData * error_o /// \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 or the value is undefined. +/// \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 or the value is undefined. +/// \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. @@ -45,53 +47,37 @@ IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value, Ika /// \param error_out \see errors.h IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double new_data, IkarusErrorData * error_out); -/// \brief Removes a data from a number value. -/// \param value The number value. -/// \pre \li Must not be null. -/// \param idx The index of the data to remove. -/// \pre \li Must be less than the size of the value. -/// \param error_out \see errors.h -IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx, IkarusErrorData * error_out); - /// \brief Inserts a data into a number value. /// \param value The number value. /// \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 -/// \remark Noop if the value is undefined. IKA_API void ikarus_number_value_clear(IkarusNumberValue * value, IkarusErrorData * error_out); -/// \brief Checks if a number value is undefined. -/// \param value The number value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_number_value_is_undefined(IkarusNumberValue const * value, IkarusErrorData * error_out); - -/// \brief Changes a number value's undefined state. -/// \param value The number value. -/// \pre \li Must not be null. -/// \param undefined The new undefined state. -/// \param error_out \see errors.h -/// \remark Noop if the value is already undefined. -/// \remark If the value is set to undefined, all data will be cleared. -/// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined, IkarusErrorData * error_out); - /// \brief Converts a number value to a string. /// \param value The number value to convert. /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. -/// \remark Undefined if the value is undefined. IKA_API char const * ikarus_number_value_to_string(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. @@ -108,7 +94,6 @@ IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusN /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The copied value. -/// \remark Must be freed with #ikarus_free. IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Converts a number value to an entity value. @@ -117,10 +102,7 @@ IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * v /// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value, IkarusErrorData * error_out); - -/// \see ikarus_toggle_value_to_value -IKA_API struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value, IkarusErrorData * error_out); +IKA_API struct IkarusValueData * ikarus_number_value_to_value(IkarusNumberValue * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index 6511eb5..3b7d978 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -5,36 +5,37 @@ #include #include -#include +#include /// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER -/// \brief A textual value. For example "Surname" or "Description" +/// \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. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create(IkarusErrorData * error_out); +IKA_API IkarusTextValue * ikarus_text_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. +/// \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 null if an error occurs or the value is undefined. -IKA_API char const * const * ikarus_text_value_get(IkarusTextValue const * value, size_t idx, IkarusErrorData * error_out); +/// \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 or the value is undefined. +/// \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. @@ -42,59 +43,41 @@ IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value, IkarusE /// \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. Ownership remains with the caller. -/// \pre \li Must not be null. +/// \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 Inserts a data into a text value. -/// \param value The text value. -/// \pre \li Must not be null. -/// \param idx The index of the data to insert. -/// \pre \li Must be less than or equal to the size of the value. -/// \param new_data The new data. Ownership remains with the caller. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -IKA_API void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data, IkarusErrorData * error_out); - /// \brief Clears a text value. /// \param value The text value. +/// \pre \li Cardinality must be "Multiple". /// \param error_out \see errors.h -/// \remark Noop if the value is undefined. IKA_API void ikarus_text_value_clear(IkarusTextValue * value, IkarusErrorData * error_out); -/// \brief Checks if a text value is undefined. -/// \param value The text value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_text_value_is_undefined(IkarusTextValue const * value, IkarusErrorData * error_out); - -/// \brief Changes a text value's undefined state. -/// \param value The text value. -/// \pre \li Must not be null. -/// \param undefined The new undefined state. -/// \param error_out \see errors.h -/// \remark Noop if the value is already undefined. -/// \remark If the value is set to undefined, all data will be cleared. -/// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined, IkarusErrorData * error_out); - /// \brief Converts a text value to a string. /// \param value The text value to convert. /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. -/// \remark Undefined if the value is undefined. IKA_API char const * ikarus_text_value_to_string(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. @@ -111,7 +94,6 @@ IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextV /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The copied value. -/// \remark Must be freed with #ikarus_free. IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Converts a text value to an entity value. @@ -120,10 +102,7 @@ IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value, /// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value, IkarusErrorData * error_out); - -/// \see ikarus_text_value_to_value -IKA_API struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value, IkarusErrorData * error_out); +IKA_API struct IkarusValueData * ikarus_text_value_to_value(IkarusTextValue * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index 9e1615b..05c5ddc 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -5,36 +5,37 @@ #include #include -#include +#include /// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER -/// \brief A true/false boolean-esque value. For example "Is Dead". +/// \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. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create(IkarusErrorData * error_out); +IKA_API IkarusToggleValue * ikarus_toggle_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. +/// \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 null if an error occurs or the value is undefined. -IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue const * value, size_t idx, IkarusErrorData * error_out); +/// \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 or the value is undefined. +/// \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. @@ -46,53 +47,37 @@ IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value, Ika /// \param error_out \see errors.h IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data, IkarusErrorData * error_out); -/// \brief Removes a data from a toggle value. -/// \param value The toggle value. -/// \pre \li Must not be null. -/// \param idx The index of the data to remove. -/// \pre \li Must be less than the size of the value. -/// \param error_out \see errors.h -IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx, IkarusErrorData * error_out); - /// \brief Inserts a data into a toggle value. /// \param value The toggle value. /// \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 -/// \remark Noop if the value is undefined. IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value, IkarusErrorData * error_out); -/// \brief Checks if a toggle value is undefined. -/// \param value The toggle value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value, IkarusErrorData * error_out); - -/// \brief Changes a toggle value's undefined state. -/// \param value The toggle value. -/// \pre \li Must not be null. -/// \param undefined The new undefined state. -/// \param error_out \see errors.h -/// \remark Noop if the value is already undefined. -/// \remark If the value is set to undefined, all data will be cleared. -/// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined, IkarusErrorData * error_out); - /// \brief Converts a toggle value to a string. /// \param value The toggle value to convert. /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. -/// \remark Undefined if the value is undefined. IKA_API char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. @@ -109,7 +94,6 @@ IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusT /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The copied value. -/// \remark Must be freed with #ikarus_free. IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Converts a toggle value to an entity value. @@ -118,10 +102,7 @@ IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * v /// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value, IkarusErrorData * error_out); - -/// \see ikarus_toggle_value_to_value -IKA_API struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value, IkarusErrorData * error_out); +IKA_API struct IkarusValueData * ikarus_toggle_value_to_value(IkarusToggleValue * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index fbc50e1..aec86ac 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -4,14 +4,27 @@ /// \author Folling /// \defgroup values Values -/// \brief The values of properties. -/// \details Each entity has a value for each property it is associated with. -/// These value classes represent plain objects. They are not associated with any entity. -/// Each value may be undefined. \see IkarusProperty -/// Values are stored as lists. If a property is "singular" then its value is a list of size 1. -/// Values are typed, with types existing for each of the corresponding property types. The data of values starts with the index 0. -/// When setting values for a property the type must match the property type and the value must be valid under the -/// property's settings. \see PropertyType +/// \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. 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 @@ -19,15 +32,19 @@ IKARUS_BEGIN_HEADER -/// \brief The common type for all values. +/// \brief The common type for all value data. struct IkarusValue; -/// \brief Visits an entity value, calling the appropriate function for the value's type. +/// \brief Visits an entity value, +/// calling the appropriate function for the value's type. /// \param value The entity value to visit. /// \pre \li Must not be null. -/// \param toggle_visitor The function to call if the value is a toggle value. Skipped if null. -/// \param number_visitor The function to call if the value is a number value. Skipped if null. -/// \param text_visitor The function to call if the value is a text value. Skipped if null. +/// \param toggle_visitor The function to call if the value is a toggle value. +/// \remark Skipped if null. +/// \param number_visitor The function to call if the value is a number value. +/// \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( @@ -39,16 +56,6 @@ IKA_API void ikarus_value_visit( IkarusErrorData * error_out ); -/// \see ikarus_value_visit -IKA_API void ikarus_value_visit_const( - IkarusValue const * value, - void (*toggle_visitor)(struct IkarusToggleValue const *, void *), - void (*number_visitor)(struct IkarusNumberValue const *, void *), - void (*text_visitor)(struct IkarusTextValue const *, void *), - void * data, - IkarusErrorData * error_out -); - IKARUS_END_HEADER /// @} diff --git a/include/ikarus/values/value_cardinality.h b/include/ikarus/values/value_cardinality.h new file mode 100644 index 0000000..6d5526f --- /dev/null +++ b/include/ikarus/values/value_cardinality.h @@ -0,0 +1,23 @@ +#pragma once + +/// \file value_cardinality.h +/// \author Folling + +/// \addtogroup values Values +/// @{ + +#include + +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 + +/// @} diff --git a/include/ikarus/values/value_type.h b/include/ikarus/values/value_type.h new file mode 100644 index 0000000..caa6165 --- /dev/null +++ b/include/ikarus/values/value_type.h @@ -0,0 +1,198 @@ +#pragma once + +/// \file value_schema.h +/// \author Folling + +/// \addtogroup values Values +/// @{ + +#include +#include +#include + +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, Number, Complex<"Foo":Toggle,"Bar":Text> +struct IkarusValueLayout; +/// \brief A fixed datapoint with a fixed layout. +/// Example: Age: Union> +struct IkarusValueLayoutConstant; +/// \brief A singular datapoint with one of a list of layouts. +/// Example: ChestReward: Combination +struct IkarusValueLayoutCombination; +/// \brief A collection of datapoints with a homogenous layout. +/// Example: Friends: List +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 + +/// @} diff --git a/src/ikarus/errors.cpp b/src/ikarus/errors.cpp index 35014f8..8f8ff3c 100644 --- a/src/ikarus/errors.cpp +++ b/src/ikarus/errors.cpp @@ -1,13 +1,5 @@ #include "ikarus/errors.h" -#include - -#include - -#include - -#include - char const * ikarus_get_error_info_name(IkarusErrorInfo info) { switch (info) { case IkarusErrorInfo_None: return "None"; diff --git a/src/ikarus/errors.hpp b/src/ikarus/errors.hpp index 0a9941f..e12a68f 100644 --- a/src/ikarus/errors.hpp +++ b/src/ikarus/errors.hpp @@ -78,16 +78,3 @@ inline void safe_strcpy(char * dest, std::string_view src, size_t dest_size) { #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__); - -#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("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); diff --git a/src/ikarus/global.cpp b/src/ikarus/global.cpp deleted file mode 100644 index 8b1929a..0000000 --- a/src/ikarus/global.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "ikarus/global.h" - -#include - -void ikarus_free(void * ptr) { - std::free(ptr); -} \ No newline at end of file diff --git a/src/ikarus/id.cpp b/src/ikarus/id.cpp deleted file mode 100644 index 9749667..0000000 --- a/src/ikarus/id.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "ikarus/id.h" - -#include - -uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; -uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; - -IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) { - return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); -} - -IkarusObjectType ikarus_id_get_object_type(IkarusId id) { - return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); -} diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index a1cf9e5..a2a5673 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -11,24 +11,27 @@ #include #include #include +#include -IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): +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_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); IKARUS_VTRYRV_OR_FAIL( - IkarusId const id, + int64_t const id, nullptr, "failed to create blueprint: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?)", IkarusObjectType_Blueprint)); - auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - TRY(db->execute("INSERT INTO `blueprints`(`id`, `name`) VALUES(?, ?)", id, name)); - return cppbase::ok(id); + project->db->transact([name](auto * db) -> cppbase::Result { + TRY(db->execute("INSERT INTO `blueprints`(`name`) VALUES(?)", name)); + return cppbase::ok(db->last_insert_rowid()); }) ); @@ -43,13 +46,13 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * erro , "unable to delete blueprint: {}", IkarusErrorInfo_Database_QueryFailed, - blueprint->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id) + blueprint->project->db->execute("DELETE FROM `blueprints` WHERE `id` = ?", blueprint->id) ); blueprint->project->uncache(blueprint); } -IkarusId ikarus_blueprint_get_id(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) { +int64_t ikarus_blueprint_get_id(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) { return ikarus::util::object_get_id(blueprint, error_out); } @@ -79,14 +82,14 @@ void ikarus_blueprint_get_properties( return; } - std::tuple ids_and_types[properties_out_size]; + std::tuple 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( - "SELECT `id` FROM `properties` WHERE `source` = ?", + blueprint->project->db->query_many_buffered( + "SELECT `id` FROM `properties` WHERE `blueprint` = ?", ids_and_types, properties_out_size, blueprint->id @@ -109,7 +112,7 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint, Ik 0, "unable to fetch blueprint property count from database: {}", IkarusErrorInfo_Database_QueryFailed, - blueprint->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", blueprint->id) + blueprint->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `blueprint` = ?", blueprint->id) ); return ret; @@ -129,13 +132,13 @@ void ikarus_blueprint_get_linked_entities( return; } - IkarusId ids[entities_out_size]; + 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( + blueprint->project->db->query_many_buffered( "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", ids, entities_out_size, @@ -157,7 +160,8 @@ size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprin 0, "unable to fetch blueprint linked entity count from database: {}", IkarusErrorInfo_Database_QueryFailed, - blueprint->project->db->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) + blueprint->project->db + ->query_one("SELECT COUNT(`entity`) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) ); return ret; diff --git a/src/ikarus/objects/blueprint.hpp b/src/ikarus/objects/blueprint.hpp index 30c57b0..cd46d62 100644 --- a/src/ikarus/objects/blueprint.hpp +++ b/src/ikarus/objects/blueprint.hpp @@ -2,9 +2,9 @@ #include -struct IkarusBlueprint : IkarusObject { +struct IkarusBlueprint : public IkarusObject { public: - IkarusBlueprint(struct IkarusProject * project, IkarusId id); + IkarusBlueprint(struct IkarusProject * project, int64_t id); IkarusBlueprint(IkarusBlueprint const &) = default; IkarusBlueprint(IkarusBlueprint &&) = default; @@ -15,7 +15,5 @@ public: ~IkarusBlueprint() override = default; public: - inline std::string_view get_table_name() const noexcept override { - return "blueprints"; - } + std::string_view get_table_name() const noexcept override; }; diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index e1dbf5a..2df1404 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -10,22 +10,38 @@ #include #include #include +#include -IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { +IkarusEntity::IkarusEntity(IkarusProject * project, int64_t id): + IkarusObject{project, id} {} + +std::string_view IkarusEntity::get_table_name() const noexcept { + return "entities"; +} + +IkarusEntity * ikarus_entity_create( + struct IkarusProject * project, + char const * name, + IkarusErrorData * error_out +) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); IKARUS_VTRYRV_OR_FAIL( - IkarusId const id, + int64_t const id, nullptr, "failed to create entity: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?)", IkarusObjectType_Entity)); - auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Entity); - TRY(db->execute("INSERT INTO `entities`(`id`, `name`) VALUES(?, ?)", id, name)); - return cppbase::ok(id); - }) + project->db->transact( + [name](auto * db + ) -> cppbase::Result { + TRY(db->execute( + "INSERT INTO `entities`(`name`) VALUES(?, ?)", + name + )); + return cppbase::ok(db->last_insert_rowid()); + } + ) ); return project->get_entity(id); @@ -39,25 +55,37 @@ void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { , "unable to delete entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", entity->id) + entity->project->db + ->execute("DELETE FROM `entities` WHERE `id` = ?", entity->id) ); entity->project->uncache(entity); } -IkarusId ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out) { +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) { +IkarusProject * ikarus_entity_get_project( + IkarusEntity const * entity, + IkarusErrorData * error_out +) { return ikarus::util::object_get_project(entity, error_out); } -char const * ikarus_entity_get_name(IkarusEntity const * entity, IkarusErrorData * error_out) { +char const * ikarus_entity_get_name( + IkarusEntity const * entity, + IkarusErrorData * error_out +) { return ikarus::util::object_get_name(entity, error_out); } -void ikarus_entity_set_name(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) { +void ikarus_entity_set_name( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out +) { ikarus::util::object_set_name(entity, name, error_out); } @@ -77,7 +105,9 @@ bool ikarus_entity_is_linked_to_blueprint( "unable to check whether entity is linked to blueprint", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_one( - "SELECT EXISTS(SELECT 1 FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?)", + "SELECT EXISTS(SELECT 1 FROM `entity_blueprint_links` WHERE " + "`entity` = ? AND " + "`blueprint` = ?)", entity->id, blueprint->id ) @@ -86,7 +116,11 @@ bool ikarus_entity_is_linked_to_blueprint( return ret; } -void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint, IkarusErrorData * error_out) { +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, ); @@ -97,14 +131,20 @@ void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBluepri "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", + "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) { +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, ); @@ -114,9 +154,26 @@ void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct IkarusBlu , "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) + 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( @@ -133,14 +190,15 @@ void ikarus_entity_get_linked_blueprints( return; } - IkarusId ids[blueprints_out_size]; + 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( - "SELECT `blueprint` FROM `entity_blueprint_links` WHERE `entity` = ?", + entity->project->db->query_many_buffered( + "SELECT `blueprint` FROM `entity_blueprint_links` WHERE `entity` = " + "?", ids, blueprints_out_size, entity->id @@ -152,7 +210,10 @@ void ikarus_entity_get_linked_blueprints( } } -size_t ikarus_entity_get_linked_blueprint_count(IkarusEntity const * entity, IkarusErrorData * error_out) { +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); @@ -161,31 +222,144 @@ size_t ikarus_entity_get_linked_blueprint_count(IkarusEntity const * entity, Ika 0, "unable to fetch entity linked blueprint count from database: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", entity->id) + entity->project->db->query_one( + "SELECT COUNT(`blueprint`) FROM `entity_blueprint_links` WHERE " + "`entity` = ?", + entity->id + ) ); return ret; } -bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out) { +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( + "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( + IkarusEntity * entity, + char const * name, + struct IkarusValue const * value, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, ); + IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); + IKARUS_FAIL_IF_NAME_INVALID(name, ); + 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 ret, + auto const has_property, false, "unable to check whether entity has property: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_one( - "SELECT EXISTS(SELECT 1 FROM `entity_properties` WHERE `entity` = ? AND `property` = ?)", + "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 ret; + return has_property; } void ikarus_entity_get_properties( @@ -202,19 +376,25 @@ void ikarus_entity_get_properties( return; } - std::tuple ids_and_types[properties_out_size]; + std::tuple 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 entity properties from database: {}", + "unable to fetch properties from entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->query_many_buffered( - "SELECT `property`, `type` FROM `properties` WHERE `source` = ?", + entity->project->db->query_many_buffered( + "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]; @@ -222,23 +402,39 @@ void ikarus_entity_get_properties( } } -size_t ikarus_entity_get_property_count(IkarusEntity const * entity, IkarusErrorData * error_out) { +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 ret, + size_t const count, 0, - "unable to fetch entity property count from database: {}", + "unable to fetch property count from entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", entity->id) + entity->project->db->query_one( + "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 ret; + return count; } -struct IkarusEntityPropertyValue * -ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out) { +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); @@ -247,25 +443,27 @@ ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const auto * value = fetch_value_from_db( entity->project, error_out, - "SELECT IFNULL((SELECT `value` FROM `values` WHERE `entity` = ? AND `property` = ?), (SELECT `default_value` FROM `properties` WHERE `id` = ?))", + "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, property->id ); IKARUS_FAIL_IF_ERROR(nullptr); - return new IkarusEntityPropertyValue{ - .entity = entity, - .property = property, - .value = value, - }; + return value; } -void ikarus_entity_set_value( +void ikarus_entity_set_property_value( IkarusEntity * entity, struct IkarusProperty const * property, - struct IkarusEntityPropertyValue * value, + struct IkarusValue * value, IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(entity, ); @@ -274,18 +472,17 @@ void ikarus_entity_set_value( IKARUS_FAIL_IF_OBJECT_MISSING(property, ); IKARUS_FAIL_IF_NULL(value, ); - auto value_json_str = boost::json::serialize(value->value->to_json()); - IKARUS_TRYRV_OR_FAIL( , "unable to set entity property value: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->execute( - "INSERT INTO `values`(`entity`, `property`, `value`) VALUES(?, ?, ?) ON CONFLICT(`entity`, `property`) DO UPDATE SET `value` = ?", + "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, - value_json_str, - value_json_str + boost::json::serialize(value->to_json()) ) ); } diff --git a/src/ikarus/objects/entity.hpp b/src/ikarus/objects/entity.hpp index 586c88f..e3bc035 100644 --- a/src/ikarus/objects/entity.hpp +++ b/src/ikarus/objects/entity.hpp @@ -2,10 +2,9 @@ #include -struct IkarusEntity : IkarusObject { +struct IkarusEntity : public IkarusObject { public: - inline IkarusEntity(struct IkarusProject * project, IkarusId id): - IkarusObject{project, id} {} + inline IkarusEntity(struct IkarusProject * project, int64_t id); IkarusEntity(IkarusEntity const &) = default; IkarusEntity(IkarusEntity &&) = default; @@ -16,7 +15,5 @@ public: ~IkarusEntity() override = default; public: - inline std::string_view get_table_name() const noexcept override { - return "entities"; - } + std::string_view get_table_name() const noexcept override; }; diff --git a/src/ikarus/objects/object.cpp b/src/ikarus/objects/object.cpp index d233783..01747cc 100644 --- a/src/ikarus/objects/object.cpp +++ b/src/ikarus/objects/object.cpp @@ -1,93 +1,7 @@ #include "object.hpp" -#include - -#include - -#include -#include -#include -#include -#include -#include #include -IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): +IkarusObject::IkarusObject(IkarusProject * project, int64_t id): project{project}, id{id} {} - -void ikarus_object_visit( - IkarusObject * object, - void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *), - void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *), - void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *), - void * data, - IkarusErrorData * error_out -) { - struct Data { - void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *); - void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *); - void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *); - void * data; - }; - - Data passthru_data{blueprint_visitor, property_visitor, entity_visitor, data}; - - ikarus_object_visit_const( - object, - [](IkarusBlueprint const * blueprint, IkarusErrorData * error_out, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->blueprint_visitor(const_cast(blueprint), error_out, passthru_data->data); - }, - [](IkarusProperty const * property, IkarusErrorData * error_out, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->property_visitor(const_cast(property), error_out, passthru_data->data); - }, - [](IkarusEntity const * entity, IkarusErrorData * error_out, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->entity_visitor(const_cast(entity), error_out, passthru_data->data); - }, - &passthru_data, - error_out - ); -} - -void ikarus_object_visit_const( - IkarusObject const * object, - void (*blueprint_visitor)(struct IkarusBlueprint const *, IkarusErrorData * error_out, void *), - void (*property_visitor)(struct IkarusProperty const *, IkarusErrorData * error_out, void *), - void (*entity_visitor)(struct IkarusEntity const *, IkarusErrorData * error_out, void *), - void * data, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(object, ); - IKARUS_FAIL_IF_OBJECT_MISSING(object, ); - - switch (ikarus_id_get_object_type(object->id)) { - case IkarusObjectType_Entity: { - auto const * entity = dynamic_cast(object); - IKARUS_FAIL_IF(entity == nullptr, , "object with entity id wasn't a entity", IkarusErrorInfo_LibIkarus_InvalidState); - entity_visitor(entity, error_out, data); - } - case IkarusObjectType_Blueprint: { - auto const * blueprint = dynamic_cast(object); - IKARUS_FAIL_IF(blueprint == nullptr, , "object with blueprint id wasn't a blueprint", IkarusErrorInfo_LibIkarus_InvalidState); - blueprint_visitor(blueprint, error_out, data); - } - case IkarusObjectType_Property: { - auto const * property = dynamic_cast(object); - IKARUS_FAIL_IF(property == nullptr, , "object with property id wasn't a property", IkarusErrorInfo_LibIkarus_InvalidState); - property_visitor(property, error_out, data); - } - default: { - IKARUS_FAIL( - , - fmt::format("unknown object type: {}", ikarus_object_type_to_string(ikarus_id_get_object_type(object->id))), - IkarusErrorInfo_LibIkarus_InvalidState - ); - } - } - - // not needed, but a good delineation of our intention if this function gets extended - IKARUS_FAIL_IF_ERROR() -} diff --git a/src/ikarus/objects/object.hpp b/src/ikarus/objects/object.hpp index 20eed59..96d5911 100644 --- a/src/ikarus/objects/object.hpp +++ b/src/ikarus/objects/object.hpp @@ -2,11 +2,9 @@ #include -#include - -struct IkarusObject { +class IkarusObject { public: - IkarusObject(struct IkarusProject * project, IkarusId id); + IkarusObject(struct IkarusProject * project, int64_t id); IkarusObject(IkarusObject const &) = default; IkarusObject(IkarusObject &&) = default; @@ -21,5 +19,18 @@ public: public: struct IkarusProject * project; - IkarusId id; + 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("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); diff --git a/src/ikarus/objects/object_type.cpp b/src/ikarus/objects/object_type.cpp deleted file mode 100644 index fb7d825..0000000 --- a/src/ikarus/objects/object_type.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "ikarus/objects/object_type.h" - -char const * ikarus_object_type_to_string(IkarusObjectType type) { - switch (type) { - case IkarusObjectType_None: return "none"; - case IkarusObjectType_Entity: return "entity"; - case IkarusObjectType_Blueprint: return "blueprint"; - case IkarusObjectType_Property: return "property"; - } - - return "unknown"; -} diff --git a/src/ikarus/objects/properties/number_property.cpp b/src/ikarus/objects/properties/number_property.cpp index 43a966c..60c9ccd 100644 --- a/src/ikarus/objects/properties/number_property.cpp +++ b/src/ikarus/objects/properties/number_property.cpp @@ -7,7 +7,7 @@ #include #include -IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, IkarusId id): +IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, int64_t id): IkarusProperty{project, id, this} {} IkarusNumberProperty * ikarus_number_property_create( diff --git a/src/ikarus/objects/properties/number_property.hpp b/src/ikarus/objects/properties/number_property.hpp index eddb189..55ff25c 100644 --- a/src/ikarus/objects/properties/number_property.hpp +++ b/src/ikarus/objects/properties/number_property.hpp @@ -1,14 +1,14 @@ #pragma once #include -#include #include +#include -struct IkarusNumberProperty : IkarusProperty { +struct IkarusNumberProperty : public IkarusProperty { public: using value_type = IkarusNumberValue; - constexpr auto static PropertyType = IkarusPropertyType_Number; + constexpr auto static PropertyType = IkarusValueType_Number; public: - IkarusNumberProperty(struct IkarusProject * project, IkarusId id); + IkarusNumberProperty(struct IkarusProject * project, int64_t id); }; diff --git a/src/ikarus/objects/properties/property.cpp b/src/ikarus/objects/properties/property.cpp index 929d3a6..ecc2f3e 100644 --- a/src/ikarus/objects/properties/property.cpp +++ b/src/ikarus/objects/properties/property.cpp @@ -5,12 +5,12 @@ #include #include #include -#include +#include #include #include #include -IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): +IkarusProperty::IkarusProperty(IkarusProject * project, int64_t id, Data data): IkarusObject{project, id}, data{data} {} @@ -28,7 +28,7 @@ IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * property->project->uncache(property); } -IkarusId ikarus_property_get_id(IkarusProperty const * property, IkarusErrorData * error_out) { +int64_t ikarus_property_get_id(IkarusProperty const * property, IkarusErrorData * error_out) { return ikarus::util::object_get_id(property, error_out); } @@ -44,19 +44,19 @@ void ikarus_property_set_name(IkarusProperty * property, char const * name, Ikar ikarus::util::object_set_name(property, name, error_out); } -IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(property, IkarusPropertyType_Toggle); - IKARUS_FAIL_IF_OBJECT_MISSING(property, IkarusPropertyType_Toggle); +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, - IkarusPropertyType_Toggle, + IkarusValueType_Toggle, "unable to fetch property type from database: {}", IkarusErrorInfo_Database_QueryFailed, property->project->db->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", property->id) ); - return static_cast(ret); + return static_cast(ret); } IkarusPropertyScope const * ikarus_property_get_scope(IkarusProperty const * property, IkarusErrorData * error_out) { @@ -68,7 +68,7 @@ IkarusPropertyScope const * ikarus_property_get_scope(IkarusProperty const * pro nullptr, "unable to fetch property source from database: {}", IkarusErrorInfo_Database_QueryFailed, - property->project->db->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) + property->project->db->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) ); switch (ikarus_id_get_object_type(source)) { @@ -83,7 +83,7 @@ IkarusPropertyScope const * ikarus_property_get_scope(IkarusProperty const * pro } } -IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out) { +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); diff --git a/src/ikarus/objects/properties/property.hpp b/src/ikarus/objects/properties/property.hpp index 5c37d7c..1fd0394 100644 --- a/src/ikarus/objects/properties/property.hpp +++ b/src/ikarus/objects/properties/property.hpp @@ -4,12 +4,12 @@ #include -struct IkarusProperty : IkarusObject { +struct IkarusProperty : public IkarusObject { public: using Data = std::variant; public: - IkarusProperty(struct IkarusProject * project, IkarusId id, Data data); + IkarusProperty(struct IkarusProject * project, int64_t id, Data data); IkarusProperty(IkarusProperty const &) = default; IkarusProperty(IkarusProperty &&) = default; diff --git a/src/ikarus/objects/properties/property_scope.cpp b/src/ikarus/objects/properties/property_scope.cpp deleted file mode 100644 index b8ec32f..0000000 --- a/src/ikarus/objects/properties/property_scope.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "property_scope.hpp" - -#include - -#include -#include - -IkarusPropertyScope::IkarusPropertyScope(Data data): - data{data} {} - -IkarusId IkarusPropertyScope::get_id() const { - return std::visit( - cppbase::overloaded{ - [](IkarusBlueprint const * blueprint) { return blueprint->id; }, - [](IkarusEntity const * entity) { return entity->id; } - }, - data - ); -} - -IkarusPropertyScope * ikarus_property_source_create_blueprint(IkarusBlueprint * blueprint) { - return new IkarusPropertyScope{blueprint}; -} - -IkarusPropertyScope * ikarus_property_source_create_entity(IkarusEntity * entity) { - return new IkarusPropertyScope{entity}; -} - -void ikarus_property_source_visit( - struct IkarusPropertyScope * property_source, - void (*blueprint_visitor)(struct IkarusBlueprint *, void *), - void (*entity_visitor)(struct IkarusEntity *, void *), - void * user_data -) { - std::visit( - cppbase::overloaded{ - [blueprint_visitor, user_data](IkarusBlueprint * blueprint) { blueprint_visitor(blueprint, user_data); }, - [entity_visitor, user_data](IkarusEntity * entity) { entity_visitor(entity, user_data); } - }, - property_source->data - ); -} - -void ikarus_property_source_visit_const( - struct IkarusPropertyScope const * property_source, - void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), - void (*entity_visitor)(struct IkarusEntity const *, void *), - void * user_data -) { - std::visit( - cppbase::overloaded{ - [blueprint_visitor, user_data](IkarusBlueprint const * blueprint) { blueprint_visitor(blueprint, user_data); }, - [entity_visitor, user_data](IkarusEntity const * entity) { entity_visitor(entity, user_data); } - }, - property_source->data - ); -} diff --git a/src/ikarus/objects/properties/property_scope.hpp b/src/ikarus/objects/properties/property_scope.hpp deleted file mode 100644 index ab8832a..0000000 --- a/src/ikarus/objects/properties/property_scope.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include - -#include -#include - -#define IKARUS_FAIL_IF_PROPERTY_SCOPE_INVALID(scope, ret) \ - std::visit( \ - cppbase::overloaded{[error_out](auto const * object) { \ - IKARUS_FAIL_IF_NULL(object, ); \ - IKARUS_FAIL_IF_OBJECT_MISSING(object, ); \ - }}, \ - scope->data \ - ); \ - \ - IKARUS_FAIL_IF_ERROR(nullptr); - -struct IkarusPropertyScope { -public: - using Data = std::variant; - -public: - explicit IkarusPropertyScope(Data data); - - IkarusPropertyScope(IkarusPropertyScope const &) = default; - IkarusPropertyScope(IkarusPropertyScope &&) = default; - - IkarusPropertyScope & operator=(IkarusPropertyScope const &) = default; - IkarusPropertyScope & operator=(IkarusPropertyScope &&) = default; - - virtual ~IkarusPropertyScope() = default; - -public: - [[nodiscard]] IkarusId get_id() const; - -public: - Data data; -}; diff --git a/src/ikarus/objects/properties/text_property.cpp b/src/ikarus/objects/properties/text_property.cpp index 6aaf483..c51d653 100644 --- a/src/ikarus/objects/properties/text_property.cpp +++ b/src/ikarus/objects/properties/text_property.cpp @@ -7,7 +7,7 @@ #include #include -IkarusTextProperty::IkarusTextProperty(IkarusProject * project, IkarusId id): +IkarusTextProperty::IkarusTextProperty(IkarusProject * project, int64_t id): IkarusProperty{project, id, this} {} IkarusTextProperty * ikarus_text_property_create( diff --git a/src/ikarus/objects/properties/text_property.hpp b/src/ikarus/objects/properties/text_property.hpp index aca2cfa..b690cda 100644 --- a/src/ikarus/objects/properties/text_property.hpp +++ b/src/ikarus/objects/properties/text_property.hpp @@ -1,14 +1,14 @@ #pragma once #include -#include +#include #include -struct IkarusTextProperty : IkarusProperty { +struct IkarusTextProperty : public IkarusProperty { public: using value_type = IkarusTextValue; - constexpr auto static PropertyType = IkarusPropertyType_Text; + constexpr auto static PropertyType = IkarusValueType_Text; public: - IkarusTextProperty(struct IkarusProject * project, IkarusId id); + IkarusTextProperty(struct IkarusProject * project, int64_t id); }; diff --git a/src/ikarus/objects/properties/toggle_property.cpp b/src/ikarus/objects/properties/toggle_property.cpp index 2449b39..d1705a2 100644 --- a/src/ikarus/objects/properties/toggle_property.cpp +++ b/src/ikarus/objects/properties/toggle_property.cpp @@ -7,7 +7,7 @@ #include #include -IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): +IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, int64_t id): IkarusProperty{project, id, this} {} IkarusToggleProperty * ikarus_toggle_property_create( diff --git a/src/ikarus/objects/properties/toggle_property.hpp b/src/ikarus/objects/properties/toggle_property.hpp index bf53c19..35575ee 100644 --- a/src/ikarus/objects/properties/toggle_property.hpp +++ b/src/ikarus/objects/properties/toggle_property.hpp @@ -1,14 +1,17 @@ #pragma once #include -#include #include +#include -struct IkarusToggleProperty : IkarusProperty { +struct IkarusToggleProperty { public: using value_type = IkarusToggleValue; - constexpr auto static PropertyType = IkarusPropertyType_Toggle; + constexpr auto static PropertyType = IkarusValueType_Toggle; public: - IkarusToggleProperty(struct IkarusProject * project, IkarusId id); + IkarusToggleProperty(struct IkarusProject * project, int64_t id); + +public: + IkarusProperty * property; }; diff --git a/src/ikarus/objects/properties/util.hpp b/src/ikarus/objects/properties/util.hpp index c095a18..97146f1 100644 --- a/src/ikarus/objects/properties/util.hpp +++ b/src/ikarus/objects/properties/util.hpp @@ -22,15 +22,14 @@ T * create_property( ) { IKARUS_FAIL_IF_NULL(project, nullptr); IKARUS_FAIL_IF_NULL(property_scope, nullptr); - IKARUS_FAIL_IF_PROPERTY_SCOPE_INVALID(property_scope, nullptr); - IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, property_scope, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); IKARUS_VTRYRV_OR_FAIL( - IkarusId const id, + int64_t const id, nullptr, "failed to create property: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->transact([name, property_scope](auto * db) -> cppbase::Result { + project->db->transact([name, property_scope](auto * db) -> cppbase::Result { 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( diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp index 1de035a..e625f5d 100644 --- a/src/ikarus/objects/util.hpp +++ b/src/ikarus/objects/util.hpp @@ -6,18 +6,12 @@ #include #include -#include -#include -#include -#include -#include -#include #include namespace ikarus::util { template -[[nodiscard]] IkarusId object_get_id(O const * object, IkarusErrorData * error_out) { +[[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); @@ -49,81 +43,14 @@ template return strdup(ret.data()); } -[[nodiscard]] inline bool name_is_unique( - IkarusProject const * project, - std::string_view name, - [[maybe_unused]] IkarusBlueprint const * blueprint, - IkarusErrorData * error_out -) { - IKARUS_VTRYRV_OR_FAIL( - bool const exists, - false, - "unable to check if blueprint name is unique", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_one("SELECT EXISTS(SELECT 1 FROM `blueprints` WHERE `name` = ?)", name) - ); - - return !exists; -} - -[[nodiscard]] inline bool name_is_unique( - IkarusProject const * project, - std::string_view name, - [[maybe_unused]] IkarusEntity const * entity, - IkarusErrorData * error_out -) { - IKARUS_VTRYRV_OR_FAIL( - bool const exists, - false, - "unable to check if entity name is unique", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_one("SELECT EXISTS(SELECT 1 FROM `entities` WHERE `name` = ?)", name) - ); - - return !exists; -} - -[[nodiscard]] inline bool -name_is_unique(IkarusProject const * project, std::string_view name, IkarusPropertyScope const * scope, IkarusErrorData * error_out) { - IKARUS_VTRYRV_OR_FAIL( - bool const exists, - false, - "unable to check if property name is unique", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_one("SELECT EXISTS(SELECT 1 FROM `properties` WHERE `name` = ? AND `scope` = ?)", name, scope->get_id()) - ); - - return !exists; -} - -[[nodiscard]] inline bool -name_is_unique(IkarusProject const * project, std::string_view name, IkarusProperty const * property, IkarusErrorData * error_out) { - std::unique_ptr scope{ikarus_property_get_scope(property, error_out)}; - IKARUS_FAIL_IF_ERROR(false); - - return name_is_unique(project, name, scope.get(), error_out); -} - -#define IKARUS_FAIL_IF_NAME_INVALID(name, ret) \ - IKARUS_FAIL_IF_NULL(name, ret); \ - IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); - -#define IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, object, ret, ...) \ - IKARUS_FAIL_IF_NAME_INVALID(name, ret); \ - IKARUS_FAIL_IF( \ - !ikarus::util::name_is_unique(project, name, object, error_out), \ - ret, \ - "name must be unique", \ - IkarusErrorInfo_Client_InvalidInput \ - ); \ - IKARUS_FAIL_IF_ERROR(ret); +#define IKARUS_FAIL_IF_NAME_INVALID(name, ret) IKARUS_FAIL_IF_NULL(name, ret); template 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_OR_DUPLICATE(name, object->project, object, ); + IKARUS_FAIL_IF_NAME_INVALID(name, ); IKARUS_TRYRV_OR_FAIL( , diff --git a/src/ikarus/persistence/migrations.hpp b/src/ikarus/persistence/migrations.hpp index a46d1ee..db649a5 100644 --- a/src/ikarus/persistence/migrations.hpp +++ b/src/ikarus/persistence/migrations.hpp @@ -8,11 +8,12 @@ #include namespace ikarus { + CPPBASE_ASSET(m0_initial_layout, "ikarus/persistence/migrations/m0_initial_layout.sql"); class Migration : public sqlitecpp::Migration { public: - Migration(char const * sql, size_t size): + Migration(char const * sql, size_t const size): sql{sql, size} {} ~Migration() override = default; diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index 9bf3cc7..aa28c8a 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -1,68 +1,51 @@ -CREATE TABLE `objects` -( - `do_not_access_rowid_alias` INTEGER PRIMARY KEY, - `type` INT NOT NULL, - `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`type` << 56)) VIRTUAL UNIQUE -) STRICT; - -CREATE UNIQUE INDEX `object_id` ON `objects` (`id`); -CREATE INDEX `object_type` ON `objects` (`type`); - CREATE TABLE `entities` ( - `id` INT, - `name` TEXT NOT NULL, + `id` INTEGER PRIMARY KEY, + `name` TEXT NOT NULL +) STRICT; - PRIMARY KEY (`id`), - UNIQUE (`name`), - FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE +CREATE TABLE `entity_values` +( + `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, + `name` TEXT NOT NULL, + `type` INT NOT NULL, + `value` TEXT NOT NULL, + + PRIMARY KEY (`entity`, `name`) ) WITHOUT ROWID, STRICT; CREATE TABLE `blueprints` ( - `id` INT, - `name` TEXT NOT NULL, - - PRIMARY KEY (`id`), - UNIQUE (`name`), - FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE -) WITHOUT ROWID, STRICT; - -CREATE TABLE `entity_blueprint_links` -( - `entity` INT NOT NULL, - `blueprint` INT NOT NULL, - - PRIMARY KEY (`entity`, `blueprint`), - FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, - FOREIGN KEY (`blueprint`) REFERENCES `blueprints` (`id`) ON DELETE CASCADE -) WITHOUT ROWID, STRICT; + `id` INTEGER PRIMARY KEY, + `name` TEXT NOT NULL +) STRICT; CREATE TABLE `properties` ( - `id` INT, - `name` TEXT NOT NULL, - `type` INT NOT NULL, - `default_value` TEXT NOT NULL, - `settings` TEXT NOT NULL, - `source` INT NOT NULL, - - PRIMARY KEY (`id`), - UNIQUE (`source`, `name`), - FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE, - FOREIGN KEY (`source`) REFERENCES `objects` (`id`) ON DELETE CASCADE -) WITHOUT ROWID, STRICT; + `id` INTEGER PRIMARY KEY, + `blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE, + `name` TEXT NOT NULL, + `type` INT NOT NULL, + `cardinality` INT NOT NULL, + `default_value` TEXT NOT NULL, + `settings` TEXT NOT NULL +) STRICT; CREATE INDEX `properties_type` ON `properties` (`type`); -CREATE INDEX `properties_source` ON `properties` (`source`); -CREATE TABLE `values` +CREATE TABLE `entity_blueprint_links` ( - `entity` INT NOT NULL, - `property` INT NOT NULL, - `value` TEXT NOT NULL, + `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, + `blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE, - PRIMARY KEY (`entity`, `property`), - FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, - FOREIGN KEY (`property`) REFERENCES `properties` (`id`) ON DELETE CASCADE + PRIMARY KEY (`entity`, `blueprint`) +) STRICT; + +CREATE TABLE `entity_property_values` +( + `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, + `property` INTEGER NOT NULL REFERENCES `properties` (`id`) ON DELETE CASCADE, + `value` TEXT NOT NULL, + + PRIMARY KEY (`entity`, `property`) ) WITHOUT ROWID, STRICT; diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 87ebef5..0d5b1e5 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -22,7 +22,7 @@ IkarusProject::IkarusProject(std::string_view name, std::string_view path, std:: _properties{}, _entities{} {} -IkarusBlueprint * IkarusProject::get_blueprint(IkarusId id) { +IkarusBlueprint * IkarusProject::get_blueprint(int64_t id) { return get_cached_object(id, this->_blueprints); } @@ -30,7 +30,7 @@ auto IkarusProject::uncache(IkarusBlueprint * blueprint) -> void { remove_cached_object(blueprint, _blueprints); } -auto IkarusProject::get_entity(IkarusId id) -> IkarusEntity * { +auto IkarusProject::get_entity(int64_t id) -> IkarusEntity * { return get_cached_object(id, this->_entities); } @@ -38,16 +38,16 @@ auto IkarusProject::uncache(IkarusEntity * entity) -> void { remove_cached_object(entity, _entities); } -auto IkarusProject::get_property(IkarusId id, IkarusPropertyType type) -> IkarusProperty * { +auto IkarusProject::get_property(int64_t id, IkarusValueType type) -> IkarusProperty * { auto const iter = _properties.find(id); if (iter == _properties.cend()) { switch (type) { - case IkarusPropertyType_Toggle: + case IkarusValueType_Toggle: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); - case IkarusPropertyType_Number: + case IkarusValueType_Number: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); - case IkarusPropertyType_Text: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); + case IkarusValueType_Text: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); } } @@ -212,13 +212,13 @@ void ikarus_project_get_blueprints( return; } - IkarusId ids[blueprints_out_size]; + int64_t ids[blueprints_out_size]; IKARUS_TRYRV_OR_FAIL( , "unable to fetch project blueprints from database: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered("SELECT `id` FROM `blueprints`", ids, blueprints_out_size) + project->db->query_many_buffered("SELECT `id` FROM `blueprints`", ids, blueprints_out_size) ); for (size_t i = 0; i < blueprints_out_size; ++i) { @@ -253,13 +253,13 @@ void ikarus_project_get_entities( return; } - IkarusId ids[entities_out_size]; + int64_t ids[entities_out_size]; IKARUS_TRYRV_OR_FAIL( , "unable to fetch project entities from database: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered("SELECT `id` FROM `entities`", ids, entities_out_size) + project->db->query_many_buffered("SELECT `id` FROM `entities`", ids, entities_out_size) ); for (size_t i = 0; i < entities_out_size; ++i) { @@ -293,7 +293,7 @@ struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * n nullptr, "unable to find entity in database: {}", IkarusErrorInfo_Client_InvalidInput, - project->db->query_one("SELECT `id` FROM `entities` WHERE `name` = ?", name) + project->db->query_one("SELECT `id` FROM `entities` WHERE `name` = ?", name) ); return project->get_entity(id); @@ -310,7 +310,7 @@ get_property_by_name(IkarusProject * project, IkarusPropertyScope * scope, char nullptr, "unable to find property in database: {}", IkarusErrorInfo_Client_InvalidInput, - project->db->query_one( + project->db->query_one( "SELECT `id`, `type` FROM `properties` WHERE `name` = ? AND `scope` = ?", name, scope->get_id() @@ -332,7 +332,7 @@ IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * n nullptr, "unable to find blueprint in database: {}", IkarusErrorInfo_Client_InvalidInput, - project->db->query_one("SELECT `id` FROM `blueprints` WHERE `name` = ?", name) + project->db->query_one("SELECT `id` FROM `blueprints` WHERE `name` = ?", name) ); return project->get_blueprint(id); diff --git a/src/ikarus/persistence/project.hpp b/src/ikarus/persistence/project.hpp index f5a00a1..1dc69dc 100644 --- a/src/ikarus/persistence/project.hpp +++ b/src/ikarus/persistence/project.hpp @@ -1,7 +1,5 @@ #pragma once -#include -#include #include #include @@ -9,8 +7,7 @@ #include #include -#include -#include +#include namespace fs = boost::filesystem; @@ -20,19 +17,19 @@ public: IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db); public: - [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; + [[nodiscard]] auto get_blueprint(int64_t id) -> struct IkarusBlueprint *; auto uncache(struct IkarusBlueprint * blueprint) -> void; - [[nodiscard]] auto get_entity(IkarusId id) -> struct IkarusEntity *; + [[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(IkarusId id, IkarusPropertyType type) -> struct IkarusProperty *; + [[nodiscard]] auto get_property(int64_t id, IkarusValueType type) -> struct IkarusProperty *; auto uncache(struct IkarusProperty * property) -> void; private: template - [[nodiscard]] T * get_cached_object(IkarusId id, auto & cache) { + [[nodiscard]] T * get_cached_object(int64_t id, auto & cache) { auto const iter = cache.find(id); if (iter == cache.cend()) { @@ -43,7 +40,7 @@ private: } template - void remove_cached_object(T * object, std::unordered_map> & cache) { + void remove_cached_object(T * object, std::unordered_map> & cache) { cache.erase(object->id); } @@ -53,9 +50,9 @@ public: std::unique_ptr db; private: - std::unordered_map> mutable _blueprints; - std::unordered_map> mutable _properties; - std::unordered_map> mutable _entities; + std::unordered_map> mutable _blueprints; + std::unordered_map> mutable _properties; + std::unordered_map> mutable _entities; }; constexpr std::string_view DB_PROJECT_NAME_KEY = "PROJECT_NAME"; diff --git a/src/ikarus/values/entity_property_value.hpp b/src/ikarus/values/entity_property_value.hpp index f75d6ba..6f70f09 100644 --- a/src/ikarus/values/entity_property_value.hpp +++ b/src/ikarus/values/entity_property_value.hpp @@ -1,7 +1 @@ #pragma once - -struct IkarusEntityPropertyValue { - struct IkarusEntity const * entity; - struct IkarusProperty const * property; - struct IkarusValue const * value; -}; diff --git a/src/ikarus/values/number_value.cpp b/src/ikarus/values/number_value.cpp index 37eab49..41fba42 100644 --- a/src/ikarus/values/number_value.cpp +++ b/src/ikarus/values/number_value.cpp @@ -6,60 +6,60 @@ #include IkarusNumberValue::IkarusNumberValue(): - IkarusValue{this} {} + IkarusValueData{this} {} -IkarusNumberValue * ikarus_number_value_create() { +IkarusNumberValue * ikarus_number_value_data_create() { return new IkarusNumberValue{}; } -double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx) { - return ikarus_value_base_get(value, idx); +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_get_size(IkarusNumberValue const * value) { - return ikarus_value_base_get_size(value); +size_t ikarus_number_value_data_get_size(IkarusNumberValue const * value) { + return ikarus_value_data_base_get_size(value); } -void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double new_data) { - return ikarus_value_base_set(value, idx, new_data); +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_remove(IkarusNumberValue * value, size_t idx) { - return ikarus_value_base_remove(value, idx); +void ikarus_number_value_data_remove(IkarusNumberValue * value, size_t idx) { + return ikarus_value_data_base_remove(value, idx); } -void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, long double new_data) { - return ikarus_value_base_insert(value, idx, new_data); +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_clear(IkarusNumberValue * value) { - return ikarus_value_base_clear(value); +void ikarus_number_value_data_clear(IkarusNumberValue * value) { + return ikarus_value_data_base_clear(value); } -bool ikarus_number_value_is_undefined(IkarusNumberValue const * value) { - return ikarus_value_base_is_undefined(value); +bool ikarus_number_value_data_is_undefined(IkarusNumberValue const * value) { + return ikarus_value_data_base_is_undefined(value); } -void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined) { - return ikarus_value_base_set_undefined(value, undefined); +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_to_string(IkarusNumberValue const * value) { - return ikarus_value_base_to_string(value, [](auto const & value) { return value; }); +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_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs) { - return ikarus_value_base_is_equal(lhs, rhs); +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_copy(IkarusNumberValue const * value) { - return ikarus_value_base_copy(value); +IkarusNumberValue * ikarus_number_value_data_copy(IkarusNumberValue const * value) { + return ikarus_value_data_base_copy(value); } -struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value) { - return ikarus_value_base_to_value(value); +struct IkarusValueData * ikarus_number_value_data_to_value(IkarusNumberValue * value) { + return ikarus_value_data_base_to_value(value); } -struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value) { - return ikarus_value_base_to_value_const(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); } diff --git a/src/ikarus/values/number_value.hpp b/src/ikarus/values/number_value.hpp index 6c6cb70..bd4bec5 100644 --- a/src/ikarus/values/number_value.hpp +++ b/src/ikarus/values/number_value.hpp @@ -5,7 +5,7 @@ #include -struct IkarusNumberValue : IkarusValue { +struct IkarusNumberValue : IkarusValueData { public: using DataType = double; diff --git a/src/ikarus/values/text_value.cpp b/src/ikarus/values/text_value.cpp index 8235c6d..9400f2e 100644 --- a/src/ikarus/values/text_value.cpp +++ b/src/ikarus/values/text_value.cpp @@ -7,60 +7,60 @@ #include IkarusTextValue::IkarusTextValue(): - IkarusValue{this} {} + IkarusValueData{this} {} -IkarusTextValue * ikarus_text_value_create() { +IkarusTextValue * ikarus_text_value_data_create() { return new IkarusTextValue{}; } -char const * ikarus_text_value_get(IkarusTextValue * value, size_t idx) { - return ikarus_value_base_get(value, idx)->data(); +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_get_size(IkarusTextValue const * value) { - return ikarus_value_base_get_size(value); +size_t ikarus_text_value_data_get_size(IkarusTextValue const * value) { + return ikarus_value_data_base_get_size(value); } -void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data) { - return ikarus_value_base_set(value, idx, new_data); +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_remove(IkarusTextValue * value, size_t idx) { - return ikarus_value_base_remove(value, idx); +void ikarus_text_value_data_remove(IkarusTextValue * value, size_t idx) { + return ikarus_value_data_base_remove(value, idx); } -void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data) { - return ikarus_value_base_insert(value, idx, new_data); +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_clear(IkarusTextValue * value) { - return ikarus_value_base_clear(value); +void ikarus_text_value_data_clear(IkarusTextValue * value) { + return ikarus_value_data_base_clear(value); } -bool ikarus_text_value_is_undefined(IkarusTextValue const * value) { - return ikarus_value_base_is_undefined(value); +bool ikarus_text_value_data_is_undefined(IkarusTextValue const * value) { + return ikarus_value_data_base_is_undefined(value); } -void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined) { - return ikarus_value_base_set_undefined(value, undefined); +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_to_string(IkarusTextValue const * value) { - return ikarus_value_base_to_string(value, [](auto const & value) { return value; }); +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_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs) { - return ikarus_value_base_is_equal(lhs, rhs); +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_copy(IkarusTextValue const * value) { - return ikarus_value_base_copy(value); +IkarusTextValue * ikarus_text_value_data_copy(IkarusTextValue const * value) { + return ikarus_value_data_base_copy(value); } -struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value) { - return ikarus_value_base_to_value(value); +struct IkarusValueData * ikarus_text_value_data_to_value(IkarusTextValue * value) { + return ikarus_value_data_base_to_value(value); } -struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value) { - return ikarus_value_base_to_value_const(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); } diff --git a/src/ikarus/values/text_value.hpp b/src/ikarus/values/text_value.hpp index 512bb8d..a1e36ea 100644 --- a/src/ikarus/values/text_value.hpp +++ b/src/ikarus/values/text_value.hpp @@ -4,7 +4,7 @@ #include -struct IkarusTextValue : IkarusValue { +struct IkarusTextValue : IkarusValueData { public: using DataType = std::string; diff --git a/src/ikarus/values/toggle_value.cpp b/src/ikarus/values/toggle_value.cpp index 7bec1f2..fb76e68 100644 --- a/src/ikarus/values/toggle_value.cpp +++ b/src/ikarus/values/toggle_value.cpp @@ -7,60 +7,60 @@ #include IkarusToggleValue::IkarusToggleValue(): - IkarusValue{this} {} + IkarusValueData{this} {} -IkarusToggleValue * ikarus_toggle_value_create() { +IkarusToggleValue * ikarus_toggle_value_data_create() { return new IkarusToggleValue{}; } -bool const * ikarus_toggle_value_get(IkarusToggleValue * value, size_t idx) { - return ikarus_value_base_get(value, idx); +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_get_size(IkarusToggleValue const * value) { - return ikarus_value_base_get_size(value); +size_t ikarus_toggle_value_data_get_size(IkarusToggleValue const * value) { + return ikarus_value_data_base_get_size(value); } -void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data) { - return ikarus_value_base_set(value, idx, new_data); +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_remove(IkarusToggleValue * value, size_t idx) { - return ikarus_value_base_remove(value, idx); +void ikarus_toggle_value_data_remove(IkarusToggleValue * value, size_t idx) { + return ikarus_value_data_base_remove(value, idx); } -void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data) { - return ikarus_value_base_insert(value, idx, new_data); +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_clear(IkarusToggleValue * value) { - return ikarus_value_base_clear(value); +void ikarus_toggle_value_data_clear(IkarusToggleValue * value) { + return ikarus_value_data_base_clear(value); } -bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value) { - return ikarus_value_base_is_undefined(value); +bool ikarus_toggle_value_data_is_undefined(IkarusToggleValue const * value) { + return ikarus_value_data_base_is_undefined(value); } -void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined) { - return ikarus_value_base_set_undefined(value, undefined); +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_to_string(IkarusToggleValue const * value) { - return ikarus_value_base_to_string(value, [](auto const & value) { return value ? "✓" : "✗"; }); +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_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs) { - return ikarus_value_base_is_equal(lhs, rhs); +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_copy(IkarusToggleValue const * value) { - return ikarus_value_base_copy(value); +IkarusToggleValue * ikarus_toggle_value_data_copy(IkarusToggleValue const * value) { + return ikarus_value_data_base_copy(value); } -struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value) { - return ikarus_value_base_to_value(value); +struct IkarusValueData * ikarus_toggle_value_data_to_value(IkarusToggleValue * value) { + return ikarus_value_data_base_to_value(value); } -struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value) { - return ikarus_value_base_to_value_const(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); } diff --git a/src/ikarus/values/toggle_value.hpp b/src/ikarus/values/toggle_value.hpp index 6882ecc..fade2c1 100644 --- a/src/ikarus/values/toggle_value.hpp +++ b/src/ikarus/values/toggle_value.hpp @@ -4,7 +4,7 @@ #include -struct IkarusToggleValue : IkarusValue { +struct IkarusToggleValue : IkarusValueData { public: using DataType = bool; diff --git a/src/ikarus/values/value.cpp b/src/ikarus/values/value.cpp index bbea7f3..de57e26 100644 --- a/src/ikarus/values/value.cpp +++ b/src/ikarus/values/value.cpp @@ -3,41 +3,53 @@ #include #include - -// required for header-only inclusion -#include "cppbase/templates.hpp" - #include -#include +// required for header-only inclusion +#include + #include #include #include #include +#include +#include -IkarusValue::IkarusValue(Data data): - data(data) {} +IkarusValue::IkarusValue(Data data, IkarusValueCardinality cardinality): + data{data}, + cardinality{cardinality} {} cppbase::Result IkarusValue::from_json(boost::json::value json) { if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); } else { - boost::int64_t const * type = nullptr; - boost::json::value const * data = nullptr; + int64_t const * type; + int64_t const * cardinality; + boost::json::value const * data; - if (auto const * type_value = obj->if_contains("type"); type_value == nullptr) { + 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 (data = obj->if_contains("data"); data == nullptr) { + 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{}); } - auto create_value = [data]() -> cppbase::Result { + if (data = obj->if_contains(IKARUS_VALUE_JSON_DATA_KEY); data == nullptr) { + return cppbase::err(FromJsonError{}); + } + + auto create_value = [data, cardinality]() -> cppbase::Result { T * ret = nullptr; + ret->cardinality = *cardinality; + if (data->is_null()) { ret = new T{}; ret->data = boost::variant2::monostate{}; @@ -58,13 +70,13 @@ cppbase::Result IkarusValue::from_jso }; switch (*type) { - case IkarusPropertyType_Toggle: { + case IkarusValueType_Toggle: { return create_value.operator()(); } - case IkarusPropertyType_Number: { + case IkarusValueType_Number: { return create_value.operator()(); } - case IkarusPropertyType_Text: { + case IkarusValueType_Text: { return create_value.operator()(); } default: { @@ -75,18 +87,18 @@ cppbase::Result IkarusValue::from_jso } boost::json::value IkarusValue::to_json() const { - auto type = boost::variant2::visit( + auto type = std::visit( cppbase::overloaded{ - []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusPropertyType_Toggle; }, - []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusPropertyType_Number; }, - []([[maybe_unused]] IkarusTextValue const * value) { return IkarusPropertyType_Text; } + []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusValueType_Toggle; }, + []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusValueType_Number; }, + []([[maybe_unused]] IkarusTextValue const * value) { return IkarusValueType_Text; } }, data ); - auto data_json = boost::variant2::visit( + auto data_json = std::visit( [](T const * value) -> boost::json::value { - return boost::variant2::visit( + 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); } @@ -98,8 +110,9 @@ boost::json::value IkarusValue::to_json() const { ); return { - {"type", type}, - {"data", data_json} + { IKARUS_VALUE_JSON_TYPE_KEY, type}, + {IKARUS_VALUE_JSON_CARDINALITY_KEY, cardinality}, + { IKARUS_VALUE_JSON_DATA_KEY, data_json} }; } @@ -110,7 +123,7 @@ void ikarus_value_visit( void (*text_visitor)(IkarusTextValue *, void *), void * data ) { - boost::variant2::visit( + std::visit( cppbase::overloaded{ [toggle_visitor, data](IkarusToggleValue * toggle_value) { toggle_visitor(toggle_value, data); }, [number_visitor, data](IkarusNumberValue * number_value) { number_visitor(number_value, data); }, @@ -119,20 +132,3 @@ void ikarus_value_visit( value->data ); } - -void ikarus_value_visit_const( - IkarusValue const * value, - void (*toggle_visitor)(IkarusToggleValue const *, void *), - void (*number_visitor)(IkarusNumberValue const *, void *), - void (*text_visitor)(IkarusTextValue const *, void *), - void * data -) { - boost::variant2::visit( - cppbase::overloaded{ - [toggle_visitor, data](IkarusToggleValue const * toggle_value) { toggle_visitor(toggle_value, data); }, - [number_visitor, data](IkarusNumberValue const * number_value) { number_visitor(number_value, data); }, - [text_visitor, data](IkarusTextValue const * text_value) { text_visitor(text_value, data); } - }, - value->data - ); -} diff --git a/src/ikarus/values/value.hpp b/src/ikarus/values/value.hpp index 1e5791b..07e92fc 100644 --- a/src/ikarus/values/value.hpp +++ b/src/ikarus/values/value.hpp @@ -1,20 +1,24 @@ #pragma once #include -#include #include #include #include +#include + +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 { public: - using Data = boost::variant2::variant; + using Data = std::variant; constexpr static auto SMALL_VEC_VALUE_SIZE = 8; public: - explicit IkarusValue(Data data); + explicit IkarusValue(Data data, IkarusValueCardinality cardinality); IkarusValue(IkarusValue const &) = default; IkarusValue(IkarusValue &&) noexcept = default; @@ -32,6 +36,7 @@ public: public: Data data; + IkarusValueCardinality cardinality; }; template<> @@ -74,3 +79,20 @@ IkarusValue * fetch_value_from_db(IkarusProject * project, IkarusErrorData * err 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( \ + "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); diff --git a/src/ikarus/values/value_base.hpp b/src/ikarus/values/value_base.hpp index d4e6e25..5fa18ae 100644 --- a/src/ikarus/values/value_base.hpp +++ b/src/ikarus/values/value_base.hpp @@ -7,9 +7,10 @@ #include template -typename V::DataType const * ikarus_value_base_get(V * value, size_t idx) { - if (auto * data = - boost::variant2::get_if>(&value->data); +typename V::DataType const * ikarus_value_data_base_get(V * value, size_t idx) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { return &(*data)[idx]; } @@ -18,9 +19,10 @@ typename V::DataType const * ikarus_value_base_get(V * value, size_t idx) { } template -size_t ikarus_value_base_get_size(V const * value) { - if (auto * data = - boost::variant2::get_if>(&value->data); +size_t ikarus_value_data_base_get_size(V const * value) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { return data->size(); } @@ -29,57 +31,61 @@ size_t ikarus_value_base_get_size(V const * value) { } template -void ikarus_value_base_set(V * value, size_t idx, typename V::DataType new_data) { - if (auto * data = - boost::variant2::get_if>(&value->data); +void ikarus_value_data_base_set(V * value, size_t idx, typename V::DataType new_data) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { (*data)[idx] = new_data; } } template -void ikarus_value_base_remove(V * value, size_t idx) { - if (auto * data = - boost::variant2::get_if>(&value->data); +void ikarus_value_data_base_remove(V * value, size_t idx) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { data->erase(data->begin() + idx); } } template -void ikarus_value_base_insert(V * value, size_t idx, typename V::DataType new_data) { - if (auto * data = - boost::variant2::get_if>(&value->data); +void ikarus_value_data_base_insert(V * value, size_t idx, typename V::DataType new_data) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { data->insert(data->begin() + idx, new_data); } } template -void ikarus_value_base_clear(V * value) { - if (auto * data = - boost::variant2::get_if>(&value->data); +void ikarus_value_data_base_clear(V * value) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { data->clear(); } } template -bool ikarus_value_base_is_undefined(V const * value) { +bool ikarus_value_data_base_is_undefined(V const * value) { return boost::variant2::holds_alternative(value->data); } template -void ikarus_value_base_set_undefined(V * value, bool undefined) { +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{}; + value->data = boost::container::small_vector{}; } } template F> -char const * ikarus_value_base_to_string(V const * value, F transformer) { +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; }, @@ -100,21 +106,21 @@ char const * ikarus_value_base_to_string(V const * value, F transformer) { } template -bool ikarus_value_base_is_equal(V const * lhs, V const * rhs) { +bool ikarus_value_data_base_is_equal(V const * lhs, V const * rhs) { return lhs->data == rhs->data; } template -V * ikarus_value_base_copy(V const * value) { +V * ikarus_value_data_base_copy(V const * value) { return new V{*value}; } template -struct IkarusValue * ikarus_value_base_to_value(V * value) { - return static_cast(value); +struct IkarusValueData * ikarus_value_data_base_to_value(V * value) { + return static_cast(value); } template -struct IkarusValue const * ikarus_value_base_to_value_const(V const * value) { - return static_cast(value); +struct IkarusValueData const * ikarus_value_data_base_to_value_data_const(V const * value) { + return static_cast(value); } From a49912337d7e12c22a9b4c751dcdd11dc6a02ed4 Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 1 Jan 2025 13:49:05 +0100 Subject: [PATCH 069/166] finalize schema/data setup Signed-off-by: Folling --- .clang-format | 6 +- .gitmodules | 3 + CMakeLists.txt | 27 +- CMakePresets.json | 35 + LICENSE.md | 2 +- docs/Flags.md | 5 + docs/Ownership.md | 7 + docs/{ => doxygen}/DoxyFile | 0 docs/{ => doxygen}/format_fix.js | 0 docs/{ => doxygen}/header.html | 0 docs/internal/Caching.md | 2 + include/CMakeLists.txt | 2 - include/ikarus/CMakeLists.txt | 1 - include/ikarus/errors.h | 148 +- include/ikarus/models/CMakeLists.txt | 41 - include/ikarus/models/blueprint.fbs | 12 - include/ikarus/models/blueprint_generated.h | 153 -- include/ikarus/models/entity.fbs | 26 - include/ikarus/models/entity_generated.h | 398 ---- include/ikarus/models/property.fbs | 14 - include/ikarus/models/property_generated.h | 289 --- include/ikarus/models/value.fbs | 85 - include/ikarus/models/value_generated.h | 1719 ----------------- include/ikarus/objects/blueprint.h | 197 +- include/ikarus/objects/entity.h | 413 ++-- .../objects/properties/number_property.h | 40 - include/ikarus/objects/properties/property.h | 144 -- .../ikarus/objects/properties/text_property.h | 40 - .../objects/properties/toggle_property.h | 37 - include/ikarus/objects/property.h | 143 ++ include/ikarus/persistence/project.h | 201 +- include/ikarus/stdtypes.h | 9 +- include/ikarus/values/data.h | 48 + include/ikarus/values/number_value.h | 109 -- include/ikarus/values/schema.h | 58 + include/ikarus/values/text_value.h | 109 -- include/ikarus/values/toggle_value.h | 109 -- include/ikarus/values/value.h | 69 +- include/ikarus/values/value_cardinality.h | 23 - include/ikarus/values/value_type.h | 198 -- include/module.modulemap | 6 - src/CMakeLists.txt | 2 +- src/ikarus/errors.cpp | 81 +- src/ikarus/errors.hpp | 205 +- src/ikarus/objects/blueprint.cpp | 163 -- src/ikarus/objects/blueprint.hpp | 28 +- src/ikarus/objects/entity.cpp | 605 ++---- src/ikarus/objects/entity.hpp | 27 +- src/ikarus/objects/object.cpp | 7 - src/ikarus/objects/object.hpp | 36 - .../objects/properties/number_property.cpp | 32 - .../objects/properties/number_property.hpp | 14 - src/ikarus/objects/properties/property.cpp | 143 -- src/ikarus/objects/properties/property.hpp | 29 - .../objects/properties/text_property.cpp | 32 - .../objects/properties/text_property.hpp | 14 - .../objects/properties/toggle_property.cpp | 32 - .../objects/properties/toggle_property.hpp | 17 - src/ikarus/objects/properties/util.hpp | 95 - src/ikarus/objects/property.cpp | 15 + src/ikarus/objects/property.hpp | 23 + src/ikarus/objects/util.hpp | 63 - src/ikarus/persistence/migrations.hpp | 13 +- .../migrations/m0_initial_layout.sql | 18 +- src/ikarus/persistence/project.cpp | 603 +++--- src/ikarus/persistence/project.hpp | 55 +- src/ikarus/values/data.cpp | 217 +++ src/ikarus/values/data.hpp | 65 + src/ikarus/values/entity_property_value.cpp | 21 - src/ikarus/values/entity_property_value.hpp | 1 - src/ikarus/values/errors.hpp | 36 + src/ikarus/values/number_value.cpp | 65 - src/ikarus/values/number_value.hpp | 25 - src/ikarus/values/schema.cpp | 177 ++ src/ikarus/values/schema.hpp | 51 + src/ikarus/values/shared.hpp | 65 + src/ikarus/values/text_value.cpp | 66 - src/ikarus/values/text_value.hpp | 24 - src/ikarus/values/toggle_value.cpp | 66 - src/ikarus/values/toggle_value.hpp | 24 - src/ikarus/values/value.cpp | 170 +- src/ikarus/values/value.hpp | 102 +- src/ikarus/values/value_base.hpp | 126 -- tests/CMakeLists.txt | 0 tools/cmake/toolchains/mac.cmake | 5 + vendor/CMakeLists.txt | 5 + vendor/catch2 | 1 + vendor/json | 1 + vendor/sqlitecpp | 2 +- 89 files changed, 2324 insertions(+), 6271 deletions(-) create mode 100644 CMakePresets.json create mode 100644 docs/Flags.md create mode 100644 docs/Ownership.md rename docs/{ => doxygen}/DoxyFile (100%) rename docs/{ => doxygen}/format_fix.js (100%) rename docs/{ => doxygen}/header.html (100%) create mode 100644 docs/internal/Caching.md delete mode 100644 include/ikarus/CMakeLists.txt delete mode 100644 include/ikarus/models/CMakeLists.txt delete mode 100644 include/ikarus/models/blueprint.fbs delete mode 100644 include/ikarus/models/blueprint_generated.h delete mode 100644 include/ikarus/models/entity.fbs delete mode 100644 include/ikarus/models/entity_generated.h delete mode 100644 include/ikarus/models/property.fbs delete mode 100644 include/ikarus/models/property_generated.h delete mode 100644 include/ikarus/models/value.fbs delete mode 100644 include/ikarus/models/value_generated.h delete mode 100644 include/ikarus/objects/properties/number_property.h delete mode 100644 include/ikarus/objects/properties/property.h delete mode 100644 include/ikarus/objects/properties/text_property.h delete mode 100644 include/ikarus/objects/properties/toggle_property.h create mode 100644 include/ikarus/objects/property.h create mode 100644 include/ikarus/values/data.h delete mode 100644 include/ikarus/values/number_value.h create mode 100644 include/ikarus/values/schema.h delete mode 100644 include/ikarus/values/text_value.h delete mode 100644 include/ikarus/values/toggle_value.h delete mode 100644 include/ikarus/values/value_cardinality.h delete mode 100644 include/ikarus/values/value_type.h delete mode 100644 include/module.modulemap delete mode 100644 src/ikarus/objects/object.cpp delete mode 100644 src/ikarus/objects/object.hpp delete mode 100644 src/ikarus/objects/properties/number_property.cpp delete mode 100644 src/ikarus/objects/properties/number_property.hpp delete mode 100644 src/ikarus/objects/properties/property.cpp delete mode 100644 src/ikarus/objects/properties/property.hpp delete mode 100644 src/ikarus/objects/properties/text_property.cpp delete mode 100644 src/ikarus/objects/properties/text_property.hpp delete mode 100644 src/ikarus/objects/properties/toggle_property.cpp delete mode 100644 src/ikarus/objects/properties/toggle_property.hpp delete mode 100644 src/ikarus/objects/properties/util.hpp create mode 100644 src/ikarus/objects/property.cpp create mode 100644 src/ikarus/objects/property.hpp delete mode 100644 src/ikarus/objects/util.hpp create mode 100644 src/ikarus/values/data.cpp create mode 100644 src/ikarus/values/data.hpp delete mode 100644 src/ikarus/values/entity_property_value.cpp delete mode 100644 src/ikarus/values/entity_property_value.hpp create mode 100644 src/ikarus/values/errors.hpp delete mode 100644 src/ikarus/values/number_value.cpp delete mode 100644 src/ikarus/values/number_value.hpp create mode 100644 src/ikarus/values/schema.cpp create mode 100644 src/ikarus/values/schema.hpp create mode 100644 src/ikarus/values/shared.hpp delete mode 100644 src/ikarus/values/text_value.cpp delete mode 100644 src/ikarus/values/text_value.hpp delete mode 100644 src/ikarus/values/toggle_value.cpp delete mode 100644 src/ikarus/values/toggle_value.hpp delete mode 100644 src/ikarus/values/value_base.hpp create mode 100644 tests/CMakeLists.txt create mode 100644 tools/cmake/toolchains/mac.cmake create mode 160000 vendor/catch2 create mode 160000 vendor/json diff --git a/.clang-format b/.clang-format index 85337cc..6d3560a 100644 --- a/.clang-format +++ b/.clang-format @@ -109,7 +109,9 @@ IncludeCategories: Priority: 11 - Regex: '^$' Priority: 12 - - Regex: '^$' + - Regex: '^$' + Priority: 13 + - Regex: '^$' Priority: 13 IndentAccessModifiers: false @@ -189,4 +191,4 @@ SpacesInSquareBrackets: false Standard: c++20 TabWidth: 4 -UseTab: Never +UseTab: Always diff --git a/.gitmodules b/.gitmodules index 933638b..94e3d41 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "vendor/cppbase"] path = vendor/cppbase url = ssh://git@git.rewritesarebliss.com:16658/Folling/cppbase.git +[submodule "vendor/json"] + path = vendor/json + url = git@github.com:nlohmann/json.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a16b08..63b949c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.31) project(ikarus) option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF) @@ -9,7 +9,7 @@ add_subdirectory(vendor) add_subdirectory(include) add_subdirectory(src) -find_package(Boost COMPONENTS system filesystem REQUIRED) +find_package(Boost CONFIG COMPONENTS system filesystem REQUIRED) add_library( ikarus SHARED @@ -17,40 +17,31 @@ add_library( ${SOURCE_FILES} ) -add_dependencies( - ikarus - flatbuffer_headers -) - set_target_properties( ikarus PROPERTIES CXX_STANDARD 23 CXX_STANDARD_REQUIRED ON + LINKER_LANGUAGE CXX POSITION_INDEPENDENT_CODE TRUE ) target_include_directories( - ikarus PUBLIC + ikarus + PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include -) - -target_include_directories( - ikarus PRIVATE + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src + ${Boost_INCLUDE_DIR} ) target_link_libraries( ikarus PRIVATE cppbase sqlitecpp + nlohmann_json::nlohmann_json ${Boost_LIBRARIES} ) -target_include_directories( - ikarus PRIVATE - ${Boost_INCLUDE_DIR} -) - if (LIBIKARUS_ENABLE_LINTS) find_program(IWYU_PATH NAMES include-what-you-use iwyu 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) add_custom_target( libikarus_docs - WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/docs + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/docs/doxygen COMMAND ${DOXYGEN_PATH} DoxyFile COMMENT "Generating documentation with Doxygen" VERBATIM diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..0772ba0 --- /dev/null +++ b/CMakePresets.json @@ -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" + } + ] +} diff --git a/LICENSE.md b/LICENSE.md index 5f2c6f6..41924e1 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -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 following conditions are met: diff --git a/docs/Flags.md b/docs/Flags.md new file mode 100644 index 0000000..6f54cdc --- /dev/null +++ b/docs/Flags.md @@ -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. \ No newline at end of file diff --git a/docs/Ownership.md b/docs/Ownership.md new file mode 100644 index 0000000..9827299 --- /dev/null +++ b/docs/Ownership.md @@ -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. \ No newline at end of file diff --git a/docs/DoxyFile b/docs/doxygen/DoxyFile similarity index 100% rename from docs/DoxyFile rename to docs/doxygen/DoxyFile diff --git a/docs/format_fix.js b/docs/doxygen/format_fix.js similarity index 100% rename from docs/format_fix.js rename to docs/doxygen/format_fix.js diff --git a/docs/header.html b/docs/doxygen/header.html similarity index 100% rename from docs/header.html rename to docs/doxygen/header.html diff --git a/docs/internal/Caching.md b/docs/internal/Caching.md new file mode 100644 index 0000000..cd4e262 --- /dev/null +++ b/docs/internal/Caching.md @@ -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. diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index e3043c7..d5a0a7a 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -5,5 +5,3 @@ file( ) set(INCLUDE_FILES ${FILES} PARENT_SCOPE) - -add_subdirectory(ikarus) \ No newline at end of file diff --git a/include/ikarus/CMakeLists.txt b/include/ikarus/CMakeLists.txt deleted file mode 100644 index cd981b8..0000000 --- a/include/ikarus/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(models) \ No newline at end of file diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index 926f07d..a41c291 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -1,19 +1,17 @@ #pragma once /// \file errors.h -/// \author Folling +/// \author Folling #include /// \addtogroup errors Errors /// \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. -/// Upon erring the function will store relevant information about the error in the out parameter. -/// If the out parameter is null nothing will be stored. This is not recommended as it essentially ignores errors. -/// For the sake of simplicity we have avoided mechanisms that "force" clients to handle errors. -/// Note that Ikarus does not check for null pointers. Passing null pointers to functions that do not explicitly state that they accept null -/// pointers is undefined behaviour. This decision is done for the sake of brevity and readability. `project_get_name(project)` is also -/// synonymous to `project->get_name()` in OOP languages, which shares the same semantics. +/// Upon erring the function will store relevant information about the error in +/// the out parameter. If the out parameter is null nothing will be stored. This +/// is not recommended as it essentially ignores errors. For the sake of +/// simplicity we have avoided mechanisms that "force" clients to handle errors. /// @{ 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 /// fault of libikarus, it just indicates where the error occurred. enum IkarusErrorInfo { - /// \brief No error occurred. - IkarusErrorInfo_None = 0x0, - /// \brief The client misused the API. - /// Example: Accessing a resource that does not exist. - IkarusErrorInfo_Client_Misuse = 0x01000001, - /// \brief The client provided a null value for a parameter that must not be null. - /// Example: Passing null for `ikarus_project_get_name` - IkarusErrorInfo_Client_InvalidNull = 0x01000002, - /// \brief The client provided an index that was out of bounds for some array. - /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3. - IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000003, - /// \brief The client provided a numeric value that was out of bounds - /// Example: Passing the value 2^32 to an i32 (might be passed as a string). - IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000004, - /// \brief The client provided invalid input that doesn't fit in any of the other categories. - /// Example: Passing an empty/blank string for a string that must be non-empty/-blank. - IkarusErrorInfo_Client_InvalidInput = 0x01000005, - /// \brief The client provided valid data in an invalid format. - /// Example: Passing a malformed JSON string. - IkarusErrorInfo_Client_InvalidFormat = 0x01000006, - /// \brief The client violated a constraint. - /// \details This error is most likely caused by endusers. - /// Example: A user tries to set the age of a character to an value outside of their specified range. - IkarusErrorInfo_Client_ConstraintViolated = 0x10000007, + /// \brief No error occurred. + IkarusErrorInfo_None = 0x0, + /// \brief The client misused the API. + /// Example: Accessing a resource that does not exist. + IkarusErrorInfo_Client_Misuse = 0x01000001, + /// \brief The client provided a null value for a parameter that must not be null. + /// Example: Passing null for `ikarus_project_get_name` + IkarusErrorInfo_Client_InvalidNull = 0x01000002, + /// \brief The client provided a non-existent resource. + /// Example: Passing an entity to a function after it has been deleted. + IkarusErrorInfo_Client_NonExistent = 0x01000003, + /// \brief The client provided an index that was out of bounds for some array. + /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3. + IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000004, + /// \brief The client provided a numeric value that was out of bounds + /// Example: Passing the value 2^32 to an i32 (might be passed as a string). + IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000005, + /// \brief The client provided invalid input that doesn't fit in any of the other categories. + /// Example: Passing an empty/blank string for a string that must be + /// non-empty/-blank. + IkarusErrorInfo_Client_InvalidInput = 0x01000006, + /// \brief The client provided valid data in an invalid format. + /// Example: Passing a malformed JSON string. + 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. - IkarusErrorInfo_Filesystem_AccessIssue = 0x03000001, - /// \brief A file was not found. - IkarusErrorInfo_Filesystem_NotFound = 0x03000002, - /// \brief A file or directory already exists. - IkarusErrorInfo_Filesystem_AlreadyExists = 0x03000003, - /// \brief Missing permissions to access a file or directory. - IkarusErrorInfo_Filesystem_MissingPermissions = 0x03000004, - /// \brief Insufficient space to perform an operation. - IkarusErrorInfo_Filesystem_InsufficientSpace = 0x03000005, - /// \brief A path is invalid. - IkarusErrorInfo_Filesystem_InvalidPath = 0x03000006, + /// \brief A file was not found. + IkarusErrorInfo_Filesystem_NotFound = 0x03000001, + /// \brief A file or directory already exists. + IkarusErrorInfo_Filesystem_AlreadyExists = 0x03000002, + /// \brief Missing permissions to access a file or directory. + IkarusErrorInfo_Filesystem_MissingPermissions = 0x03000003, + /// \brief Insufficient space to perform an operation. + IkarusErrorInfo_Filesystem_InsufficientSpace = 0x03000004, + /// \brief A path is invalid. + IkarusErrorInfo_Filesystem_InvalidPath = 0x03000005, - /// \brief A database connection failed. - IkarusErrorInfo_Database_ConnectionFailed = 0x04000001, - /// \brief A database query failed. - IkarusErrorInfo_Database_QueryFailed = 0x04000002, - /// \brief A database migration failed. - IkarusErrorInfo_Database_MigrationFailed = 0x04000003, - /// \brief A database is in an invalid state. This indicates a corrupt project. - /// Example: An entity is linked to a non-existant blueprint. - IkarusErrorInfo_Database_InvalidState = 0x04000004, + /// \brief A database connection failed. + IkarusErrorInfo_Database_ConnectionFailed = 0x04000001, + /// \brief A database query failed. + IkarusErrorInfo_Database_QueryFailed = 0x04000002, + /// \brief A database migration failed. + IkarusErrorInfo_Database_MigrationFailed = 0x04000003, + /// \brief A database is in an invalid state. This indicates a corrupt project. + /// Example: An entity is linked to a non-existent blueprint. + IkarusErrorInfo_Database_InvalidState = 0x04000004, - /// \brief A system call failed. - IkarusErrorInfo_OS_SystemCallFailed = 0x05000001, - /// \brief A system call returned an invalid value. - IkarusErrorInfo_OS_InvalidReturnValue = 0x05000002, - /// \brief An OOM error occurred. - IkarusErrorInfo_OS_InsufficientMemory = 0x05000003, + /// \brief A system call failed. + IkarusErrorInfo_OS_SystemCallFailed = 0x05000001, + /// \brief A system call returned an invalid value. + IkarusErrorInfo_OS_InvalidReturnValue = 0x05000002, + /// \brief An OOM error occurred. + IkarusErrorInfo_OS_InsufficientMemory = 0x05000003, - /// \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, - /// whereas the former may imply that the state is ephemeral, e.g. data within a function. - /// Example: The name of an object is found to be invalid UTF8. - IkarusErrorInfo_LibIkarus_InvalidState = 0x06000001, - /// \brief libikarus is unable to perform a certain operation that should succeed. - IkarusErrorInfo_LibIkarus_CannotPerformOperation = 0x06000002, - /// \brief libikarus is unable to perform a certain operation within a given timeframe. - /// Example: A query takes longer than the timeout. - IkarusErrorInfo_LibIkarus_Timeout = 0x06000003, + /// \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, + /// whereas the former may imply that the state is ephemeral, e.g. data + /// within a function. + /// Example: The name of an object is found to be invalid UTF8. + IkarusErrorInfo_LibIkarus_InvalidState = 0x06000001, + /// \brief libikarus is unable to perform a certain operation that should succeed. + IkarusErrorInfo_LibIkarus_CannotPerformOperation = 0x06000002, + /// \brief libikarus is unable to perform a certain operation within a given timeframe. + /// Example: A query takes longer than the timeout. + IkarusErrorInfo_LibIkarus_Timeout = 0x06000003, }; /// \brief The maximum length of an error message. @@ -96,17 +98,17 @@ enum IkarusErrorInfo { /// \brief The data stored for an error struct IkarusErrorData { - /// \brief The error type - IkarusErrorInfo info; + /// \brief The error type + 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. /// \param info The error info to get the name of. /// \return The name of the error info. /// \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. /// \param data The error data to check. diff --git a/include/ikarus/models/CMakeLists.txt b/include/ikarus/models/CMakeLists.txt deleted file mode 100644 index e3d8758..0000000 --- a/include/ikarus/models/CMakeLists.txt +++ /dev/null @@ -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 -) diff --git a/include/ikarus/models/blueprint.fbs b/include/ikarus/models/blueprint.fbs deleted file mode 100644 index fb782d8..0000000 --- a/include/ikarus/models/blueprint.fbs +++ /dev/null @@ -1,12 +0,0 @@ -include "property.fbs"; - -namespace Ikarus; - -table Blueprint { - id: int64; - name: string; - description: string; - tags: [string]; -} - -root_type Blueprint; diff --git a/include/ikarus/models/blueprint_generated.h b/include/ikarus/models/blueprint_generated.h deleted file mode 100644 index b70924b..0000000 --- a/include/ikarus/models/blueprint_generated.h +++ /dev/null @@ -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(VT_ID, 0); - } - const ::flatbuffers::String *name() const { - return GetPointer(VT_NAME); - } - const ::flatbuffers::String *description() const { - return GetPointer(VT_DESCRIPTION); - } - const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { - return GetPointer> *>(VT_TAGS); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(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(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 Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset 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 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(buf); -} - -inline const Ikarus::Blueprint *GetSizePrefixedBlueprint(const void *buf) { - return ::flatbuffers::GetSizePrefixedRoot(buf); -} - -inline bool VerifyBlueprintBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifyBuffer(nullptr); -} - -inline bool VerifySizePrefixedBlueprintBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifySizePrefixedBuffer(nullptr); -} - -inline void FinishBlueprintBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.Finish(root); -} - -inline void FinishSizePrefixedBlueprintBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.FinishSizePrefixed(root); -} - -} // namespace Ikarus - -#endif // FLATBUFFERS_GENERATED_BLUEPRINT_IKARUS_H_ diff --git a/include/ikarus/models/entity.fbs b/include/ikarus/models/entity.fbs deleted file mode 100644 index bce2ff5..0000000 --- a/include/ikarus/models/entity.fbs +++ /dev/null @@ -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; diff --git a/include/ikarus/models/entity_generated.h b/include/ikarus/models/entity_generated.h deleted file mode 100644 index e253038..0000000 --- a/include/ikarus/models/entity_generated.h +++ /dev/null @@ -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(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 - 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(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 value) { - fbb_.AddOffset(NamedValue::VT_VALUE, value); - } - explicit NamedValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - fbb_.Required(o, NamedValue::VT_NAME); - return o; - } -}; - -inline ::flatbuffers::Offset CreateNamedValue( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::String> name = 0, - ::flatbuffers::Offset 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 CreateNamedValueDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const char *name = nullptr, - ::flatbuffers::Offset 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(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(property_id() > _property_id) - static_cast(property_id() < _property_id); - } - Ikarus::Value::Data data_type() const { - return static_cast(GetField(VT_DATA_TYPE, 0)); - } - const void *data() const { - return GetPointer(VT_DATA); - } - template const T *data_as() const; - const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { - return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { - return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { - return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::SimpleData *data_as_SimpleData() const { - return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::CombinedData *data_as_CombinedData() const { - return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ListData *data_as_ListData() const { - return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ComplexData *data_as_ComplexData() const { - return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_PROPERTY_ID, 8) && - VerifyField(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() const { - return data_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *PropertyValue::data_as() const { - return data_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *PropertyValue::data_as() const { - return data_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *PropertyValue::data_as() const { - return data_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *PropertyValue::data_as() const { - return data_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *PropertyValue::data_as() const { - return data_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *PropertyValue::data_as() 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(PropertyValue::VT_PROPERTY_ID, property_id, 0); - } - void add_data_type(Ikarus::Value::Data data_type) { - fbb_.AddElement(PropertyValue::VT_DATA_TYPE, static_cast(data_type), 0); - } - void add_data(::flatbuffers::Offset data) { - fbb_.AddOffset(PropertyValue::VT_DATA, data); - } - explicit PropertyValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreatePropertyValue( - ::flatbuffers::FlatBufferBuilder &_fbb, - int64_t property_id = 0, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset 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(VT_ID, 0); - } - const ::flatbuffers::String *name() const { - return GetPointer(VT_NAME); - } - const ::flatbuffers::String *description() const { - return GetPointer(VT_DESCRIPTION); - } - const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { - return GetPointer> *>(VT_TAGS); - } - const ::flatbuffers::Vector<::flatbuffers::Offset> *values() const { - return GetPointer> *>(VT_VALUES); - } - const ::flatbuffers::Vector<::flatbuffers::Offset> *property_values() const { - return GetPointer> *>(VT_PROPERTY_VALUES); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(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(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>> values) { - fbb_.AddOffset(Entity::VT_VALUES, values); - } - void add_property_values(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> property_values) { - fbb_.AddOffset(Entity::VT_PROPERTY_VALUES, property_values); - } - explicit EntityBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset 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>> values = 0, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> 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 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> *values = nullptr, - std::vector<::flatbuffers::Offset> *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(values) : 0; - auto property_values__ = property_values ? _fbb.CreateVectorOfSortedTables(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(buf); -} - -inline const Ikarus::Entity *GetSizePrefixedEntity(const void *buf) { - return ::flatbuffers::GetSizePrefixedRoot(buf); -} - -inline bool VerifyEntityBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifyBuffer(nullptr); -} - -inline bool VerifySizePrefixedEntityBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifySizePrefixedBuffer(nullptr); -} - -inline void FinishEntityBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.Finish(root); -} - -inline void FinishSizePrefixedEntityBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.FinishSizePrefixed(root); -} - -} // namespace Ikarus - -#endif // FLATBUFFERS_GENERATED_ENTITY_IKARUS_H_ diff --git a/include/ikarus/models/property.fbs b/include/ikarus/models/property.fbs deleted file mode 100644 index 8a96bb2..0000000 --- a/include/ikarus/models/property.fbs +++ /dev/null @@ -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; diff --git a/include/ikarus/models/property_generated.h b/include/ikarus/models/property_generated.h deleted file mode 100644 index 1111bee..0000000 --- a/include/ikarus/models/property_generated.h +++ /dev/null @@ -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(VT_ID, 0); - } - const ::flatbuffers::String *name() const { - return GetPointer(VT_NAME); - } - const ::flatbuffers::String *description() const { - return GetPointer(VT_DESCRIPTION); - } - const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { - return GetPointer> *>(VT_TAGS); - } - Ikarus::Value::Schema value_schema_type() const { - return static_cast(GetField(VT_VALUE_SCHEMA_TYPE, 0)); - } - const void *value_schema() const { - return GetPointer(VT_VALUE_SCHEMA); - } - template 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(value_schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *value_schema_as_SimpleSchema() const { - return value_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(value_schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *value_schema_as_CombinedSchema() const { - return value_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(value_schema()) : nullptr; - } - const Ikarus::Value::ListSchema *value_schema_as_ListSchema() const { - return value_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(value_schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *value_schema_as_ComplexSchema() const { - return value_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(value_schema()) : nullptr; - } - Ikarus::Value::Data default_value_type() const { - return static_cast(GetField(VT_DEFAULT_VALUE_TYPE, 0)); - } - const void *default_value() const { - return GetPointer(VT_DEFAULT_VALUE); - } - template 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(default_value()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *default_value_as_NumberDataPoint() const { - return default_value_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(default_value()) : nullptr; - } - const Ikarus::Value::TextDataPoint *default_value_as_TextDataPoint() const { - return default_value_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(default_value()) : nullptr; - } - const Ikarus::Value::SimpleData *default_value_as_SimpleData() const { - return default_value_type() == Ikarus::Value::Data::SimpleData ? static_cast(default_value()) : nullptr; - } - const Ikarus::Value::CombinedData *default_value_as_CombinedData() const { - return default_value_type() == Ikarus::Value::Data::CombinedData ? static_cast(default_value()) : nullptr; - } - const Ikarus::Value::ListData *default_value_as_ListData() const { - return default_value_type() == Ikarus::Value::Data::ListData ? static_cast(default_value()) : nullptr; - } - const Ikarus::Value::ComplexData *default_value_as_ComplexData() const { - return default_value_type() == Ikarus::Value::Data::ComplexData ? static_cast(default_value()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(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(verifier, VT_VALUE_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_VALUE_SCHEMA) && - VerifySchema(verifier, value_schema(), value_schema_type()) && - VerifyField(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() const { - return value_schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *Property::value_schema_as() const { - return value_schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *Property::value_schema_as() const { - return value_schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *Property::value_schema_as() const { - return value_schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *Property::value_schema_as() const { - return value_schema_as_ComplexSchema(); -} - -template<> inline const Ikarus::Value::ToggleDataPoint *Property::default_value_as() const { - return default_value_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *Property::default_value_as() const { - return default_value_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *Property::default_value_as() const { - return default_value_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *Property::default_value_as() const { - return default_value_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *Property::default_value_as() const { - return default_value_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *Property::default_value_as() const { - return default_value_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *Property::default_value_as() 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(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(Property::VT_VALUE_SCHEMA_TYPE, static_cast(value_schema_type), 0); - } - void add_value_schema(::flatbuffers::Offset value_schema) { - fbb_.AddOffset(Property::VT_VALUE_SCHEMA, value_schema); - } - void add_default_value_type(Ikarus::Value::Data default_value_type) { - fbb_.AddElement(Property::VT_DEFAULT_VALUE_TYPE, static_cast(default_value_type), 0); - } - void add_default_value(::flatbuffers::Offset default_value) { - fbb_.AddOffset(Property::VT_DEFAULT_VALUE, default_value); - } - explicit PropertyBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset 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 value_schema = 0, - Ikarus::Value::Data default_value_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset 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 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 value_schema = 0, - Ikarus::Value::Data default_value_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset 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(buf); -} - -inline const Ikarus::Property *GetSizePrefixedProperty(const void *buf) { - return ::flatbuffers::GetSizePrefixedRoot(buf); -} - -inline bool VerifyPropertyBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifyBuffer(nullptr); -} - -inline bool VerifySizePrefixedPropertyBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifySizePrefixedBuffer(nullptr); -} - -inline void FinishPropertyBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.Finish(root); -} - -inline void FinishSizePrefixedPropertyBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.FinishSizePrefixed(root); -} - -} // namespace Ikarus - -#endif // FLATBUFFERS_GENERATED_PROPERTY_IKARUS_H_ diff --git a/include/ikarus/models/value.fbs b/include/ikarus/models/value.fbs deleted file mode 100644 index 27a27e9..0000000 --- a/include/ikarus/models/value.fbs +++ /dev/null @@ -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; diff --git a/include/ikarus/models/value_generated.h b/include/ikarus/models/value_generated.h deleted file mode 100644 index c9da9e3..0000000 --- a/include/ikarus/models/value_generated.h +++ /dev/null @@ -1,1719 +0,0 @@ -// automatically generated by the FlatBuffers compiler, do not modify - - -#ifndef FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_H_ -#define FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_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"); - -namespace Ikarus { -namespace Value { - -struct ToggleDataPoint; -struct ToggleDataPointBuilder; - -struct NumberDataPoint; -struct NumberDataPointBuilder; - -struct TextDataPoint; -struct TextDataPointBuilder; - -struct ConstantSchema; -struct ConstantSchemaBuilder; - -struct SimpleSchema; -struct SimpleSchemaBuilder; - -struct SimpleData; -struct SimpleDataBuilder; - -struct CombinedSchema; -struct CombinedSchemaBuilder; - -struct CombinedData; -struct CombinedDataBuilder; - -struct ListSchema; -struct ListSchemaBuilder; - -struct ListData; -struct ListDataBuilder; - -struct NamedSchema; -struct NamedSchemaBuilder; - -struct ComplexSchema; -struct ComplexSchemaBuilder; - -struct NamedData; -struct NamedDataBuilder; - -struct ComplexData; -struct ComplexDataBuilder; - -struct Value; -struct ValueBuilder; - -enum class Data : uint8_t { - NONE = 0, - ToggleDataPoint = 1, - NumberDataPoint = 2, - TextDataPoint = 3, - SimpleData = 4, - CombinedData = 5, - ListData = 6, - ComplexData = 7, - MIN = NONE, - MAX = ComplexData -}; - -inline const Data (&EnumValuesData())[8] { - static const Data values[] = { - Data::NONE, - Data::ToggleDataPoint, - Data::NumberDataPoint, - Data::TextDataPoint, - Data::SimpleData, - Data::CombinedData, - Data::ListData, - Data::ComplexData - }; - return values; -} - -inline const char * const *EnumNamesData() { - static const char * const names[9] = { - "NONE", - "ToggleDataPoint", - "NumberDataPoint", - "TextDataPoint", - "SimpleData", - "CombinedData", - "ListData", - "ComplexData", - nullptr - }; - return names; -} - -inline const char *EnumNameData(Data e) { - if (::flatbuffers::IsOutRange(e, Data::NONE, Data::ComplexData)) return ""; - const size_t index = static_cast(e); - return EnumNamesData()[index]; -} - -template struct DataTraits { - static const Data enum_value = Data::NONE; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::ToggleDataPoint; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::NumberDataPoint; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::TextDataPoint; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::SimpleData; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::CombinedData; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::ListData; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::ComplexData; -}; - -bool VerifyData(::flatbuffers::Verifier &verifier, const void *obj, Data type); -bool VerifyDataVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); - -enum class Schema : uint8_t { - NONE = 0, - ConstantSchema = 1, - SimpleSchema = 2, - CombinedSchema = 3, - ListSchema = 4, - ComplexSchema = 5, - MIN = NONE, - MAX = ComplexSchema -}; - -inline const Schema (&EnumValuesSchema())[6] { - static const Schema values[] = { - Schema::NONE, - Schema::ConstantSchema, - Schema::SimpleSchema, - Schema::CombinedSchema, - Schema::ListSchema, - Schema::ComplexSchema - }; - return values; -} - -inline const char * const *EnumNamesSchema() { - static const char * const names[7] = { - "NONE", - "ConstantSchema", - "SimpleSchema", - "CombinedSchema", - "ListSchema", - "ComplexSchema", - nullptr - }; - return names; -} - -inline const char *EnumNameSchema(Schema e) { - if (::flatbuffers::IsOutRange(e, Schema::NONE, Schema::ComplexSchema)) return ""; - const size_t index = static_cast(e); - return EnumNamesSchema()[index]; -} - -template struct SchemaTraits { - static const Schema enum_value = Schema::NONE; -}; - -template<> struct SchemaTraits { - static const Schema enum_value = Schema::ConstantSchema; -}; - -template<> struct SchemaTraits { - static const Schema enum_value = Schema::SimpleSchema; -}; - -template<> struct SchemaTraits { - static const Schema enum_value = Schema::CombinedSchema; -}; - -template<> struct SchemaTraits { - static const Schema enum_value = Schema::ListSchema; -}; - -template<> struct SchemaTraits { - static const Schema enum_value = Schema::ComplexSchema; -}; - -bool VerifySchema(::flatbuffers::Verifier &verifier, const void *obj, Schema type); -bool VerifySchemaVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); - -struct ToggleDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ToggleDataPointBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA = 4 - }; - const ::flatbuffers::Vector *data() const { - return GetPointer *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - verifier.EndTable(); - } -}; - -struct ToggleDataPointBuilder { - typedef ToggleDataPoint Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data(::flatbuffers::Offset<::flatbuffers::Vector> data) { - fbb_.AddOffset(ToggleDataPoint::VT_DATA, data); - } - explicit ToggleDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateToggleDataPoint( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector> data = 0) { - ToggleDataPointBuilder builder_(_fbb); - builder_.add_data(data); - return builder_.Finish(); -} - -struct ToggleDataPoint::Traits { - using type = ToggleDataPoint; - static auto constexpr Create = CreateToggleDataPoint; -}; - -inline ::flatbuffers::Offset CreateToggleDataPointDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *data = nullptr) { - auto data__ = data ? _fbb.CreateVector(*data) : 0; - return Ikarus::Value::CreateToggleDataPoint( - _fbb, - data__); -} - -struct NumberDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef NumberDataPointBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA = 4 - }; - const ::flatbuffers::Vector *data() const { - return GetPointer *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - verifier.EndTable(); - } -}; - -struct NumberDataPointBuilder { - typedef NumberDataPoint Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data(::flatbuffers::Offset<::flatbuffers::Vector> data) { - fbb_.AddOffset(NumberDataPoint::VT_DATA, data); - } - explicit NumberDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateNumberDataPoint( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector> data = 0) { - NumberDataPointBuilder builder_(_fbb); - builder_.add_data(data); - return builder_.Finish(); -} - -struct NumberDataPoint::Traits { - using type = NumberDataPoint; - static auto constexpr Create = CreateNumberDataPoint; -}; - -inline ::flatbuffers::Offset CreateNumberDataPointDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *data = nullptr) { - auto data__ = data ? _fbb.CreateVector(*data) : 0; - return Ikarus::Value::CreateNumberDataPoint( - _fbb, - data__); -} - -struct TextDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef TextDataPointBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA = 4 - }; - const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *data() const { - return GetPointer> *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - verifier.VerifyVectorOfStrings(data()) && - verifier.EndTable(); - } -}; - -struct TextDataPointBuilder { - typedef TextDataPoint Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> data) { - fbb_.AddOffset(TextDataPoint::VT_DATA, data); - } - explicit TextDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateTextDataPoint( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> data = 0) { - TextDataPointBuilder builder_(_fbb); - builder_.add_data(data); - return builder_.Finish(); -} - -struct TextDataPoint::Traits { - using type = TextDataPoint; - static auto constexpr Create = CreateTextDataPoint; -}; - -inline ::flatbuffers::Offset CreateTextDataPointDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector<::flatbuffers::Offset<::flatbuffers::String>> *data = nullptr) { - auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::String>>(*data) : 0; - return Ikarus::Value::CreateTextDataPoint( - _fbb, - data__); -} - -struct ConstantSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ConstantSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SUB_SCHEMA_TYPE = 4, - VT_SUB_SCHEMA = 6, - VT_DATA_TYPE = 8, - VT_DATA = 10 - }; - Ikarus::Value::Schema sub_schema_type() const { - return static_cast(GetField(VT_SUB_SCHEMA_TYPE, 0)); - } - const void *sub_schema() const { - return GetPointer(VT_SUB_SCHEMA); - } - template const T *sub_schema_as() const; - const Ikarus::Value::ConstantSchema *sub_schema_as_ConstantSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *sub_schema_as_SimpleSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *sub_schema_as_CombinedSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::ListSchema *sub_schema_as_ListSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *sub_schema_as_ComplexSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(sub_schema()) : nullptr; - } - Ikarus::Value::Data data_type() const { - return static_cast(GetField(VT_DATA_TYPE, 0)); - } - const void *data() const { - return GetPointer(VT_DATA); - } - template const T *data_as() const; - const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { - return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { - return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { - return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::SimpleData *data_as_SimpleData() const { - return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::CombinedData *data_as_CombinedData() const { - return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ListData *data_as_ListData() const { - return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ComplexData *data_as_ComplexData() const { - return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_SUB_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_SUB_SCHEMA) && - VerifySchema(verifier, sub_schema(), sub_schema_type()) && - VerifyField(verifier, VT_DATA_TYPE, 1) && - VerifyOffset(verifier, VT_DATA) && - VerifyData(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ConstantSchema *ConstantSchema::sub_schema_as() const { - return sub_schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *ConstantSchema::sub_schema_as() const { - return sub_schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *ConstantSchema::sub_schema_as() const { - return sub_schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *ConstantSchema::sub_schema_as() const { - return sub_schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *ConstantSchema::sub_schema_as() const { - return sub_schema_as_ComplexSchema(); -} - -template<> inline const Ikarus::Value::ToggleDataPoint *ConstantSchema::data_as() const { - return data_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *ConstantSchema::data_as() const { - return data_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *ConstantSchema::data_as() const { - return data_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *ConstantSchema::data_as() const { - return data_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *ConstantSchema::data_as() const { - return data_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *ConstantSchema::data_as() const { - return data_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *ConstantSchema::data_as() const { - return data_as_ComplexData(); -} - -struct ConstantSchemaBuilder { - typedef ConstantSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_sub_schema_type(Ikarus::Value::Schema sub_schema_type) { - fbb_.AddElement(ConstantSchema::VT_SUB_SCHEMA_TYPE, static_cast(sub_schema_type), 0); - } - void add_sub_schema(::flatbuffers::Offset sub_schema) { - fbb_.AddOffset(ConstantSchema::VT_SUB_SCHEMA, sub_schema); - } - void add_data_type(Ikarus::Value::Data data_type) { - fbb_.AddElement(ConstantSchema::VT_DATA_TYPE, static_cast(data_type), 0); - } - void add_data(::flatbuffers::Offset data) { - fbb_.AddOffset(ConstantSchema::VT_DATA, data); - } - explicit ConstantSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateConstantSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - Ikarus::Value::Schema sub_schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset sub_schema = 0, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset data = 0) { - ConstantSchemaBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_sub_schema(sub_schema); - builder_.add_data_type(data_type); - builder_.add_sub_schema_type(sub_schema_type); - return builder_.Finish(); -} - -struct ConstantSchema::Traits { - using type = ConstantSchema; - static auto constexpr Create = CreateConstantSchema; -}; - -struct SimpleSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef SimpleSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SUB_SCHEMA_TYPE = 4, - VT_SUB_SCHEMA = 6 - }; - Ikarus::Value::Schema sub_schema_type() const { - return static_cast(GetField(VT_SUB_SCHEMA_TYPE, 0)); - } - const void *sub_schema() const { - return GetPointer(VT_SUB_SCHEMA); - } - template const T *sub_schema_as() const; - const Ikarus::Value::ConstantSchema *sub_schema_as_ConstantSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *sub_schema_as_SimpleSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *sub_schema_as_CombinedSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::ListSchema *sub_schema_as_ListSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *sub_schema_as_ComplexSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(sub_schema()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_SUB_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_SUB_SCHEMA) && - VerifySchema(verifier, sub_schema(), sub_schema_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ConstantSchema *SimpleSchema::sub_schema_as() const { - return sub_schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *SimpleSchema::sub_schema_as() const { - return sub_schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *SimpleSchema::sub_schema_as() const { - return sub_schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *SimpleSchema::sub_schema_as() const { - return sub_schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *SimpleSchema::sub_schema_as() const { - return sub_schema_as_ComplexSchema(); -} - -struct SimpleSchemaBuilder { - typedef SimpleSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_sub_schema_type(Ikarus::Value::Schema sub_schema_type) { - fbb_.AddElement(SimpleSchema::VT_SUB_SCHEMA_TYPE, static_cast(sub_schema_type), 0); - } - void add_sub_schema(::flatbuffers::Offset sub_schema) { - fbb_.AddOffset(SimpleSchema::VT_SUB_SCHEMA, sub_schema); - } - explicit SimpleSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateSimpleSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - Ikarus::Value::Schema sub_schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset sub_schema = 0) { - SimpleSchemaBuilder builder_(_fbb); - builder_.add_sub_schema(sub_schema); - builder_.add_sub_schema_type(sub_schema_type); - return builder_.Finish(); -} - -struct SimpleSchema::Traits { - using type = SimpleSchema; - static auto constexpr Create = CreateSimpleSchema; -}; - -struct SimpleData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef SimpleDataBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA_TYPE = 4, - VT_DATA = 6 - }; - Ikarus::Value::Data data_type() const { - return static_cast(GetField(VT_DATA_TYPE, 0)); - } - const void *data() const { - return GetPointer(VT_DATA); - } - template const T *data_as() const; - const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { - return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { - return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { - return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::SimpleData *data_as_SimpleData() const { - return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::CombinedData *data_as_CombinedData() const { - return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ListData *data_as_ListData() const { - return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ComplexData *data_as_ComplexData() const { - return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_DATA_TYPE, 1) && - VerifyOffset(verifier, VT_DATA) && - VerifyData(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ToggleDataPoint *SimpleData::data_as() const { - return data_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *SimpleData::data_as() const { - return data_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *SimpleData::data_as() const { - return data_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *SimpleData::data_as() const { - return data_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *SimpleData::data_as() const { - return data_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *SimpleData::data_as() const { - return data_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *SimpleData::data_as() const { - return data_as_ComplexData(); -} - -struct SimpleDataBuilder { - typedef SimpleData Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data_type(Ikarus::Value::Data data_type) { - fbb_.AddElement(SimpleData::VT_DATA_TYPE, static_cast(data_type), 0); - } - void add_data(::flatbuffers::Offset data) { - fbb_.AddOffset(SimpleData::VT_DATA, data); - } - explicit SimpleDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateSimpleData( - ::flatbuffers::FlatBufferBuilder &_fbb, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset data = 0) { - SimpleDataBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_data_type(data_type); - return builder_.Finish(); -} - -struct SimpleData::Traits { - using type = SimpleData; - static auto constexpr Create = CreateSimpleData; -}; - -struct CombinedSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef CombinedSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SCHEMAS_TYPE = 4, - VT_SCHEMAS = 6 - }; - const ::flatbuffers::Vector *schemas_type() const { - return GetPointer *>(VT_SCHEMAS_TYPE); - } - const ::flatbuffers::Vector<::flatbuffers::Offset> *schemas() const { - return GetPointer> *>(VT_SCHEMAS); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_SCHEMAS_TYPE) && - verifier.VerifyVector(schemas_type()) && - VerifyOffset(verifier, VT_SCHEMAS) && - verifier.VerifyVector(schemas()) && - VerifySchemaVector(verifier, schemas(), schemas_type()) && - verifier.EndTable(); - } -}; - -struct CombinedSchemaBuilder { - typedef CombinedSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_schemas_type(::flatbuffers::Offset<::flatbuffers::Vector> schemas_type) { - fbb_.AddOffset(CombinedSchema::VT_SCHEMAS_TYPE, schemas_type); - } - void add_schemas(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas) { - fbb_.AddOffset(CombinedSchema::VT_SCHEMAS, schemas); - } - explicit CombinedSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateCombinedSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector> schemas_type = 0, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas = 0) { - CombinedSchemaBuilder builder_(_fbb); - builder_.add_schemas(schemas); - builder_.add_schemas_type(schemas_type); - return builder_.Finish(); -} - -struct CombinedSchema::Traits { - using type = CombinedSchema; - static auto constexpr Create = CreateCombinedSchema; -}; - -inline ::flatbuffers::Offset CreateCombinedSchemaDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *schemas_type = nullptr, - const std::vector<::flatbuffers::Offset> *schemas = nullptr) { - auto schemas_type__ = schemas_type ? _fbb.CreateVector(*schemas_type) : 0; - auto schemas__ = schemas ? _fbb.CreateVector<::flatbuffers::Offset>(*schemas) : 0; - return Ikarus::Value::CreateCombinedSchema( - _fbb, - schemas_type__, - schemas__); -} - -struct CombinedData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef CombinedDataBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA_TYPE = 4, - VT_DATA = 6 - }; - const ::flatbuffers::Vector *data_type() const { - return GetPointer *>(VT_DATA_TYPE); - } - const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { - return GetPointer> *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA_TYPE) && - verifier.VerifyVector(data_type()) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - VerifyDataVector(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -struct CombinedDataBuilder { - typedef CombinedData Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data_type(::flatbuffers::Offset<::flatbuffers::Vector> data_type) { - fbb_.AddOffset(CombinedData::VT_DATA_TYPE, data_type); - } - void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { - fbb_.AddOffset(CombinedData::VT_DATA, data); - } - explicit CombinedDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateCombinedData( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector> data_type = 0, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { - CombinedDataBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_data_type(data_type); - return builder_.Finish(); -} - -struct CombinedData::Traits { - using type = CombinedData; - static auto constexpr Create = CreateCombinedData; -}; - -inline ::flatbuffers::Offset CreateCombinedDataDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *data_type = nullptr, - const std::vector<::flatbuffers::Offset> *data = nullptr) { - auto data_type__ = data_type ? _fbb.CreateVector(*data_type) : 0; - auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; - return Ikarus::Value::CreateCombinedData( - _fbb, - data_type__, - data__); -} - -struct ListSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ListSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SCHEMA_TYPE = 4, - VT_SCHEMA = 6 - }; - Ikarus::Value::Schema schema_type() const { - return static_cast(GetField(VT_SCHEMA_TYPE, 0)); - } - const void *schema() const { - return GetPointer(VT_SCHEMA); - } - template const T *schema_as() const; - const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { - return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { - return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { - return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ListSchema *schema_as_ListSchema() const { - return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { - return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_SCHEMA) && - VerifySchema(verifier, schema(), schema_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ConstantSchema *ListSchema::schema_as() const { - return schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *ListSchema::schema_as() const { - return schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *ListSchema::schema_as() const { - return schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *ListSchema::schema_as() const { - return schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *ListSchema::schema_as() const { - return schema_as_ComplexSchema(); -} - -struct ListSchemaBuilder { - typedef ListSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_schema_type(Ikarus::Value::Schema schema_type) { - fbb_.AddElement(ListSchema::VT_SCHEMA_TYPE, static_cast(schema_type), 0); - } - void add_schema(::flatbuffers::Offset schema) { - fbb_.AddOffset(ListSchema::VT_SCHEMA, schema); - } - explicit ListSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateListSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset schema = 0) { - ListSchemaBuilder builder_(_fbb); - builder_.add_schema(schema); - builder_.add_schema_type(schema_type); - return builder_.Finish(); -} - -struct ListSchema::Traits { - using type = ListSchema; - static auto constexpr Create = CreateListSchema; -}; - -struct ListData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ListDataBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA_TYPE = 4, - VT_DATA = 6 - }; - const ::flatbuffers::Vector *data_type() const { - return GetPointer *>(VT_DATA_TYPE); - } - const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { - return GetPointer> *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA_TYPE) && - verifier.VerifyVector(data_type()) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - VerifyDataVector(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -struct ListDataBuilder { - typedef ListData Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data_type(::flatbuffers::Offset<::flatbuffers::Vector> data_type) { - fbb_.AddOffset(ListData::VT_DATA_TYPE, data_type); - } - void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { - fbb_.AddOffset(ListData::VT_DATA, data); - } - explicit ListDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateListData( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector> data_type = 0, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { - ListDataBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_data_type(data_type); - return builder_.Finish(); -} - -struct ListData::Traits { - using type = ListData; - static auto constexpr Create = CreateListData; -}; - -inline ::flatbuffers::Offset CreateListDataDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *data_type = nullptr, - const std::vector<::flatbuffers::Offset> *data = nullptr) { - auto data_type__ = data_type ? _fbb.CreateVector(*data_type) : 0; - auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; - return Ikarus::Value::CreateListData( - _fbb, - data_type__, - data__); -} - -struct NamedSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef NamedSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_NAME = 4, - VT_SCHEMA_TYPE = 6, - VT_SCHEMA = 8 - }; - const ::flatbuffers::String *name() const { - return GetPointer(VT_NAME); - } - Ikarus::Value::Schema schema_type() const { - return static_cast(GetField(VT_SCHEMA_TYPE, 0)); - } - const void *schema() const { - return GetPointer(VT_SCHEMA); - } - template const T *schema_as() const; - const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { - return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { - return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { - return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ListSchema *schema_as_ListSchema() const { - return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { - return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_NAME) && - verifier.VerifyString(name()) && - VerifyField(verifier, VT_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_SCHEMA) && - VerifySchema(verifier, schema(), schema_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ConstantSchema *NamedSchema::schema_as() const { - return schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *NamedSchema::schema_as() const { - return schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *NamedSchema::schema_as() const { - return schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *NamedSchema::schema_as() const { - return schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *NamedSchema::schema_as() const { - return schema_as_ComplexSchema(); -} - -struct NamedSchemaBuilder { - typedef NamedSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { - fbb_.AddOffset(NamedSchema::VT_NAME, name); - } - void add_schema_type(Ikarus::Value::Schema schema_type) { - fbb_.AddElement(NamedSchema::VT_SCHEMA_TYPE, static_cast(schema_type), 0); - } - void add_schema(::flatbuffers::Offset schema) { - fbb_.AddOffset(NamedSchema::VT_SCHEMA, schema); - } - explicit NamedSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateNamedSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::String> name = 0, - Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset schema = 0) { - NamedSchemaBuilder builder_(_fbb); - builder_.add_schema(schema); - builder_.add_name(name); - builder_.add_schema_type(schema_type); - return builder_.Finish(); -} - -struct NamedSchema::Traits { - using type = NamedSchema; - static auto constexpr Create = CreateNamedSchema; -}; - -inline ::flatbuffers::Offset CreateNamedSchemaDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const char *name = nullptr, - Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset schema = 0) { - auto name__ = name ? _fbb.CreateString(name) : 0; - return Ikarus::Value::CreateNamedSchema( - _fbb, - name__, - schema_type, - schema); -} - -struct ComplexSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ComplexSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SCHEMAS = 4 - }; - const ::flatbuffers::Vector<::flatbuffers::Offset> *schemas() const { - return GetPointer> *>(VT_SCHEMAS); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_SCHEMAS) && - verifier.VerifyVector(schemas()) && - verifier.VerifyVectorOfTables(schemas()) && - verifier.EndTable(); - } -}; - -struct ComplexSchemaBuilder { - typedef ComplexSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_schemas(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas) { - fbb_.AddOffset(ComplexSchema::VT_SCHEMAS, schemas); - } - explicit ComplexSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateComplexSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas = 0) { - ComplexSchemaBuilder builder_(_fbb); - builder_.add_schemas(schemas); - return builder_.Finish(); -} - -struct ComplexSchema::Traits { - using type = ComplexSchema; - static auto constexpr Create = CreateComplexSchema; -}; - -inline ::flatbuffers::Offset CreateComplexSchemaDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector<::flatbuffers::Offset> *schemas = nullptr) { - auto schemas__ = schemas ? _fbb.CreateVector<::flatbuffers::Offset>(*schemas) : 0; - return Ikarus::Value::CreateComplexSchema( - _fbb, - schemas__); -} - -struct NamedData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef NamedDataBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_NAME = 4, - VT_DATA_TYPE = 6, - VT_DATA = 8 - }; - const ::flatbuffers::String *name() const { - return GetPointer(VT_NAME); - } - Ikarus::Value::Data data_type() const { - return static_cast(GetField(VT_DATA_TYPE, 0)); - } - const void *data() const { - return GetPointer(VT_DATA); - } - template const T *data_as() const; - const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { - return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { - return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { - return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::SimpleData *data_as_SimpleData() const { - return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::CombinedData *data_as_CombinedData() const { - return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ListData *data_as_ListData() const { - return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ComplexData *data_as_ComplexData() const { - return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_NAME) && - verifier.VerifyString(name()) && - VerifyField(verifier, VT_DATA_TYPE, 1) && - VerifyOffset(verifier, VT_DATA) && - VerifyData(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ToggleDataPoint *NamedData::data_as() const { - return data_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *NamedData::data_as() const { - return data_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *NamedData::data_as() const { - return data_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *NamedData::data_as() const { - return data_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *NamedData::data_as() const { - return data_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *NamedData::data_as() const { - return data_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *NamedData::data_as() const { - return data_as_ComplexData(); -} - -struct NamedDataBuilder { - typedef NamedData Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { - fbb_.AddOffset(NamedData::VT_NAME, name); - } - void add_data_type(Ikarus::Value::Data data_type) { - fbb_.AddElement(NamedData::VT_DATA_TYPE, static_cast(data_type), 0); - } - void add_data(::flatbuffers::Offset data) { - fbb_.AddOffset(NamedData::VT_DATA, data); - } - explicit NamedDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateNamedData( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::String> name = 0, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset data = 0) { - NamedDataBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_name(name); - builder_.add_data_type(data_type); - return builder_.Finish(); -} - -struct NamedData::Traits { - using type = NamedData; - static auto constexpr Create = CreateNamedData; -}; - -inline ::flatbuffers::Offset CreateNamedDataDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const char *name = nullptr, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset data = 0) { - auto name__ = name ? _fbb.CreateString(name) : 0; - return Ikarus::Value::CreateNamedData( - _fbb, - name__, - data_type, - data); -} - -struct ComplexData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ComplexDataBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA = 4 - }; - const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { - return GetPointer> *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - verifier.VerifyVectorOfTables(data()) && - verifier.EndTable(); - } -}; - -struct ComplexDataBuilder { - typedef ComplexData Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { - fbb_.AddOffset(ComplexData::VT_DATA, data); - } - explicit ComplexDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateComplexData( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { - ComplexDataBuilder builder_(_fbb); - builder_.add_data(data); - return builder_.Finish(); -} - -struct ComplexData::Traits { - using type = ComplexData; - static auto constexpr Create = CreateComplexData; -}; - -inline ::flatbuffers::Offset CreateComplexDataDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector<::flatbuffers::Offset> *data = nullptr) { - auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; - return Ikarus::Value::CreateComplexData( - _fbb, - data__); -} - -struct Value FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ValueBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SCHEMA_TYPE = 4, - VT_SCHEMA = 6, - VT_DATA_TYPE = 8, - VT_DATA = 10 - }; - Ikarus::Value::Schema schema_type() const { - return static_cast(GetField(VT_SCHEMA_TYPE, 0)); - } - const void *schema() const { - return GetPointer(VT_SCHEMA); - } - template const T *schema_as() const; - const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { - return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { - return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { - return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ListSchema *schema_as_ListSchema() const { - return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { - return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; - } - Ikarus::Value::Data data_type() const { - return static_cast(GetField(VT_DATA_TYPE, 0)); - } - const void *data() const { - return GetPointer(VT_DATA); - } - template const T *data_as() const; - const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { - return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { - return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { - return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::SimpleData *data_as_SimpleData() const { - return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::CombinedData *data_as_CombinedData() const { - return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ListData *data_as_ListData() const { - return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ComplexData *data_as_ComplexData() const { - return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_SCHEMA) && - VerifySchema(verifier, schema(), schema_type()) && - VerifyField(verifier, VT_DATA_TYPE, 1) && - VerifyOffset(verifier, VT_DATA) && - VerifyData(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ConstantSchema *Value::schema_as() const { - return schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *Value::schema_as() const { - return schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *Value::schema_as() const { - return schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *Value::schema_as() const { - return schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *Value::schema_as() const { - return schema_as_ComplexSchema(); -} - -template<> inline const Ikarus::Value::ToggleDataPoint *Value::data_as() const { - return data_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *Value::data_as() const { - return data_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *Value::data_as() const { - return data_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *Value::data_as() const { - return data_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *Value::data_as() const { - return data_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *Value::data_as() const { - return data_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *Value::data_as() const { - return data_as_ComplexData(); -} - -struct ValueBuilder { - typedef Value Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_schema_type(Ikarus::Value::Schema schema_type) { - fbb_.AddElement(Value::VT_SCHEMA_TYPE, static_cast(schema_type), 0); - } - void add_schema(::flatbuffers::Offset schema) { - fbb_.AddOffset(Value::VT_SCHEMA, schema); - } - void add_data_type(Ikarus::Value::Data data_type) { - fbb_.AddElement(Value::VT_DATA_TYPE, static_cast(data_type), 0); - } - void add_data(::flatbuffers::Offset data) { - fbb_.AddOffset(Value::VT_DATA, data); - } - explicit ValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateValue( - ::flatbuffers::FlatBufferBuilder &_fbb, - Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset schema = 0, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset data = 0) { - ValueBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_schema(schema); - builder_.add_data_type(data_type); - builder_.add_schema_type(schema_type); - return builder_.Finish(); -} - -struct Value::Traits { - using type = Value; - static auto constexpr Create = CreateValue; -}; - -inline bool VerifyData(::flatbuffers::Verifier &verifier, const void *obj, Data type) { - switch (type) { - case Data::NONE: { - return true; - } - case Data::ToggleDataPoint: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::NumberDataPoint: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::TextDataPoint: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::SimpleData: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::CombinedData: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::ListData: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::ComplexData: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - default: return true; - } -} - -inline bool VerifyDataVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { - if (!values || !types) return !values && !types; - if (values->size() != types->size()) return false; - for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { - if (!VerifyData( - verifier, values->Get(i), types->GetEnum(i))) { - return false; - } - } - return true; -} - -inline bool VerifySchema(::flatbuffers::Verifier &verifier, const void *obj, Schema type) { - switch (type) { - case Schema::NONE: { - return true; - } - case Schema::ConstantSchema: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Schema::SimpleSchema: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Schema::CombinedSchema: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Schema::ListSchema: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Schema::ComplexSchema: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - default: return true; - } -} - -inline bool VerifySchemaVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { - if (!values || !types) return !values && !types; - if (values->size() != types->size()) return false; - for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { - if (!VerifySchema( - verifier, values->Get(i), types->GetEnum(i))) { - return false; - } - } - return true; -} - -inline const Ikarus::Value::Value *GetValue(const void *buf) { - return ::flatbuffers::GetRoot(buf); -} - -inline const Ikarus::Value::Value *GetSizePrefixedValue(const void *buf) { - return ::flatbuffers::GetSizePrefixedRoot(buf); -} - -inline bool VerifyValueBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifyBuffer(nullptr); -} - -inline bool VerifySizePrefixedValueBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifySizePrefixedBuffer(nullptr); -} - -inline void FinishValueBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.Finish(root); -} - -inline void FinishSizePrefixedValueBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.FinishSizePrefixed(root); -} - -} // namespace Value -} // namespace Ikarus - -#endif // FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_H_ diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 9ad444e..adc97e9 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -1,7 +1,7 @@ #pragma once /// \file blueprint.h -/// \author Folling +/// \author Folling #include #include @@ -13,57 +13,113 @@ IKARUS_BEGIN_HEADER -/// \brief Blueprints are templates for managing common properties of entities. -/// \details A blueprint is a collection of properties which can be linked to entities. -/// Each entity the blueprint is linked to will have values for the blueprints -/// properties. Changes in blueprints will be reflected in all linked entities. +/// \brief Templates for sharing common properties between entities. +/// \details A blueprint allows sharing properties between entities. +/// Each entity the blueprint is linked to will have a corresponding value for +/// each of the blueprint's properties. Changes in blueprints will be reflected +/// in all linked entities. struct IkarusBlueprint; -/// \brief Creates a blueprint. -/// \param project The project the blueprint is part of. +/// \brief Flags for creating a blueprint. +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 exist. /// \param name The name of the blueprint. /// \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. +/// \param error_out \see errors.h +/// \remark Ownership remains with libikarus. IKA_API IkarusBlueprint * ikarus_blueprint_create( - struct IkarusProject * project, - char const * name, - IkarusErrorData * error_out + struct IkarusProject * project, + char const * name, + 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. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param flags Flags for deleting the blueprint. +/// \remark Must not be used after deletion. /// \param error_out \see errors.h -/// \remark The blueprint must not be accessed after deletion. IKA_API void ikarus_blueprint_delete( - IkarusBlueprint * blueprint, - IkarusErrorData * error_out + struct IkarusBlueprint * blueprint, + IkarusBlueprintDeleteFlags flags, + IkarusErrorData * error_out ); -/// \brief Gets the ID of a blueprint. -/// \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. +/// \brief Gets the project a blueprint belongs to. /// \param blueprint The blueprint to get the project of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The project or null if an error occurs. -IKA_API IkarusProject * ikarus_blueprint_get_project( - IkarusBlueprint const * blueprint, - IkarusErrorData * error_out +/// \return The project the blueprint belongs to. +/// \remark Ownership remains with libikarus. +IKA_API struct IkarusProject * ikarus_blueprint_get_project( + struct IkarusBlueprint * blueprint, + struct IkarusErrorData * error_out ); /// \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 exist. /// \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( - IkarusBlueprint const * blueprint, - IkarusErrorData * error_out + struct IkarusBlueprint * blueprint, + 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. /// \param blueprint The blueprint to set the name of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param name The new name of the blueprint. /// \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 IKA_API void ikarus_blueprint_set_name( - IkarusBlueprint * blueprint, - char const * name, - IkarusErrorData * error_out + struct IkarusBlueprint * blueprint, + char const * name, + IkarusBlueprintSetNameFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param properties_out The buffer to write the properties to. -/// \pre \li Must not be null. -/// \param properties_out_size The size of the buffer. +/// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs. /// \param error_out \see errors.h -/// \see ikarus_blueprint_get_property_count -IKA_API void ikarus_blueprint_get_properties( - IkarusBlueprint const * blueprint, - struct IkarusProperty ** properties_out, - size_t properties_out_size, - IkarusErrorData * error_out +/// \return The properties of the blueprint or null if an error occurs. +IKA_API struct IkarusProperty ** ikarus_blueprint_get_properties( + struct IkarusBlueprint * blueprint, + size_t * size_out, + struct IkarusErrorData * error_out ); /// \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 exist. /// \param error_out \see errors.h -/// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_property_count( - IkarusBlueprint const * blueprint, - IkarusErrorData * error_out +/// \return The number of properties of the blueprint or 0 if an error occurs. +IKA_API size_t ikarus_blueprint_get_properties_count( + struct IkarusBlueprint * blueprint, + struct IkarusErrorData * error_out ); -/// \brief Gets the entities linked to a blueprint. -/// \param blueprint The blueprint to get the linked entities of. +/// \brief Gets all entities linked to a blueprint. +/// \param blueprint The blueprint to get the entities of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param entities_out The buffer to write the entities to. -/// \pre \li Must not be null. -/// \param entities_out_size The size of the buffer. +/// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs. +/// \remark Ignore if null. /// \param error_out \see errors.h -/// \see ikarus_blueprint_get_linked_entity_count -IKA_API void ikarus_blueprint_get_linked_entities( - IkarusBlueprint const * blueprint, - struct IkarusEntity ** entities_out, - size_t entities_out_size, - IkarusErrorData * error_out +/// \return The entities linked to the blueprint or null if an error occurs. +IKA_API struct IkarusEntity ** ikarus_blueprint_get_entities( + struct IkarusBlueprint * blueprint, + size_t * size_out, + struct IkarusErrorData * error_out ); /// \brief Gets the number of entities linked to a blueprint. -/// \param blueprint The blueprint to get the number of linked entities of. +/// \param blueprint The blueprint to get the entities count of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The number of linked entities or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_linked_entity_count( - IkarusBlueprint const * blueprint, - IkarusErrorData * error_out +/// \return The number of entities linked to the blueprint or 0 if an error occurs. +IKA_API size_t ikarus_blueprint_get_entities_count( + struct IkarusBlueprint * blueprint, + struct IkarusErrorData * error_out ); IKARUS_END_HEADER -// @} +/// @} diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 28cebf8..0481da7 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -5,7 +5,7 @@ #include /// \file entity.h -/// \author Folling +/// \author Folling /// \defgroup entities Entities /// \brief Entities are the core building blocks of Ikarus. @@ -13,15 +13,13 @@ IKARUS_BEGIN_HEADER /// \brief Entities are the core building blocks of Ikarus. -/// \details Entities store two data: A name and a set of values. -/// The name identifies the entity but does not have to be unique. The values -/// are the actual information of the entity. +/// \details Entities store a number of values associated with a name or property. /// -/// 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. /// 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`. /// `EntityValues` are a direct part of the entity. @@ -29,59 +27,89 @@ IKARUS_BEGIN_HEADER /// via a blueprint. struct IkarusEntity; -/// \brief Creates an entity. -/// \param project The project the entity is part of. +/// \brief Flags for creating an entity. +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 exist. /// \param name The name of the entity. /// \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 /// \return The created entity or null if an error occurs. IKA_API IkarusEntity * ikarus_entity_create( - struct IkarusProject * project, - char const * name, - IkarusErrorData * error_out + struct IkarusProject * project, + char const * name, + 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. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param flags Flags for deleting the entity. /// \param error_out \see errors.h -/// \remark The entity must not be accessed after deletion. -IKA_API void -ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); +IKA_API void ikarus_entity_delete( + IkarusEntity * entity, + IkarusEntityDeleteFlags flags, + IkarusErrorData * error_out +); -/// \brief Gets the id of an entity. -/// \param entity The entity to get the id of. +/// \brief Flags for copying an entity. +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 exist. +/// \param flags Flags for copying the entity. /// \param error_out \see errors.h -/// \return The id of the entity or 0 if an error occurs. -IKA_API int64_t -ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out); +/// \return The copied entity or null if an error occurs. +IKA_API IkarusEntity * ikarus_entity_copy( + 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. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The project the entity is part of or null if an error occurs. -IKA_API IkarusProject * ikarus_entity_get_project( - IkarusEntity const * entity, - IkarusErrorData * error_out -); +/// \return The project the entity belongs to. +IKA_API struct IkarusProject * +ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out); /// \brief Gets the name of an entity. /// \param entity The entity 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 entity or null if an error occurs. -IKA_API char const * ikarus_entity_get_name( - IkarusEntity const * entity, - IkarusErrorData * error_out -); +/// \return The name of the entity. +IKA_API char const * +ikarus_entity_get_name(IkarusEntity * entity, 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. /// \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. /// \param name The new name of the entity. /// \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 IKA_API void ikarus_entity_set_name( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out + IkarusEntity * entity, + char const * name, + IkarusEntitySetNameFlags flags, + IkarusErrorData * error_out ); -/// \brief Checks if an entity is linked to a blueprint. -/// \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. +/// \brief Gets the blueprints an entity is linked to. /// \param entity The entity to get the blueprints of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param blueprints_out The buffer to write the blueprints to. -/// \pre \li Must not be null. -/// \param blueprints_out_size The size of the buffer. +/// \param size_out An out parameter for the number of blueprints in the returned array +/// or undefined if an error occurs. /// \param error_out \see errors.h -/// \see ikarus_entity_get_linked_blueprint_count -IKA_API void ikarus_entity_get_linked_blueprints( - IkarusEntity const * entity, - struct IkarusBlueprint ** blueprints_out, - size_t blueprints_out_size, - IkarusErrorData * error_out +IKA_API struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( + IkarusEntity * entity, + size_t * size_out, + IkarusErrorData * error_out ); /// \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 exist. /// \param error_out \see errors.h -/// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_entity_get_linked_blueprint_count( - IkarusEntity const * entity, - IkarusErrorData * error_out +/// \return The number of linked blueprints or 0 if an error occurs. +IKA_API size_t ikarus_entity_get_linked_blueprints_count( + IkarusEntity * entity, + IkarusErrorData * error_out ); -/// \brief Checks if an entity has a value with a given name. -/// \param entity The entity to check. +/// \brief Flags for linking an entity to a blueprint. +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 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 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 -/// \return False if the entity does not have a value associated with -/// the name or if an error occurs, true otherwise. -IKA_API bool ikarus_entity_has_value( - IkarusEntity const * entity, - char const * name, - IkarusErrorData * error_out +IKA_API void ikarus_entity_link_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusEntityLinkBlueprintFlags flags, + 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. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param name The name of the value to get. -/// \pre \li Must not be null. -/// \pre \li Must be an existing value of the entity. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The value's name. +/// \pre \li Must not be null. +/// \remark Ownership remains with the client. /// \param error_out \see errors.h -/// \return The value of the entity or null if an error occurs. -IKA_API struct IkarusValue * ikarus_entity_get_value( - IkarusEntity const * entity, - char const * name, - IkarusErrorData * error_out +/// \return The value, in json format of or null if an error occurs. \see value.h +IKA_API char const * ikarus_entity_get_value( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out ); -/// \brief Sets the value of an entity. -/// If the entity does not have a value associated with the name, it is created. -/// \remark Types are overwritten if the value already exists. +/// \brief Flags for setting a value of an entity. +enum IkarusEntitySetValueFlags { + /// \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. /// \pre \li Must not be null. /// \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 exist. -/// \param value The new value of the entity. +/// \param value The value to set. /// \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 IKA_API void ikarus_entity_set_value( - IkarusEntity * entity, - char const * name, - struct IkarusValue const * value, - IkarusErrorData * error_out + IkarusEntity * entity, + char const * name, + char const * value, + IkarusEntitySetValueFlags flags, + IkarusErrorData * error_out ); -/// \brief Removes a value from an entity. -/// \pre \li The value must exist. +/// \brief Flags for deleting a value of an entity. +enum IkarusEntityDeleteValueFlags { + /// \brief No flags. + IkarusEntityDeleteValueFlags_None = 0, +}; + +/// \brief Deletes a value of an entity. /// \param entity The entity to delete the value of. /// \pre \li Must not be null. /// \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 be the name of a value which exists within the property. +/// \param flags Flags for deleting the value. /// \param error_out \see errors.h -IKA_API void ikarus_entity_remove_value( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out +IKA_API void ikarus_entity_delete_value( + IkarusEntity * entity, + char const * name, + IkarusEntityDeleteValueFlags flags, + IkarusErrorData * error_out ); -/// \brief Checks if a property is linked to an entity. -/// \param entity The entity to check. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param property The property to check. +/// \brief Gets the property values of an entity. +/// \param entity The entity to get the property values of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return False if an error occurs or the entity does not have the property, -/// true otherwise. -IKA_API bool ikarus_entity_has_property( - IkarusEntity const * entity, - struct IkarusProperty const * property, - 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 +/// \return The property values, in msgpack format of or null if an error occurs. +/// The format is a map of property pointers (as integers) to values. \see +/// value.h +IKA_API char const * ikarus_entity_get_property_values( + IkarusEntity * entity, + IkarusErrorData * error_out ); /// \brief Gets the value of a property of an entity. -/// \details If the entity has never set the value of the property, -/// the property's default value is returned. -/// \param entity The entity to get the value of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param property The property to get the value of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \pre \li Must be linked to the entity. -/// \param error_out \see errors.h -/// \return The value of the property or null if an error occurs. -IKA_API struct IkarusValue * ikarus_entity_get_property_value( - IkarusEntity const * entity, - struct IkarusProperty const * property, - IkarusErrorData * error_out +/// \param entity The entity to get the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to get the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \pre \li Must be linked to the entity. +/// \param error_out \see errors.h +/// \return The value, in json format of or null if an error occurs. \see +/// value.h +IKA_API char const * ikarus_entity_get_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + 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. -/// \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 exist. /// \param property The property to set the value of. /// \pre \li Must not be null. /// \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 be of the same type as the property. -/// \pre \li Must be valid for the property's settings. +/// \pre \li Must be a valid json representation of a value. \see value.h +/// \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 -/// \remark If the entity does not have the property, this function fails. IKA_API void ikarus_entity_set_property_value( - IkarusEntity * entity, - struct IkarusProperty const * property, - struct IkarusValue const * value, - IkarusErrorData * error_out + IkarusEntity * entity, + struct IkarusProperty * property, + char const * value, + IkarusEntitySetPropertyValueFlags flags, + IkarusErrorData * error_out ); IKARUS_END_HEADER -// @} +/// @} diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h deleted file mode 100644 index d989ec2..0000000 --- a/include/ikarus/objects/properties/number_property.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -/// \file number_property.h -/// \author Folling - -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h deleted file mode 100644 index c7ba19b..0000000 --- a/include/ikarus/objects/properties/property.h +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -/// \file property.h -/// \author Folling - -#include -#include -#include -#include -#include - -/// \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 - -// @} diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h deleted file mode 100644 index a0c0a9d..0000000 --- a/include/ikarus/objects/properties/text_property.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -/// \file text_property.h -/// \author Folling - -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h deleted file mode 100644 index bfec311..0000000 --- a/include/ikarus/objects/properties/toggle_property.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -/// \file toggle_property.h -/// \author Folling - -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h new file mode 100644 index 0000000..5bff42c --- /dev/null +++ b/include/ikarus/objects/property.h @@ -0,0 +1,143 @@ +#pragma once + +/// \file property.h +/// \author Folling + +#include +#include +#include + +/// \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 + +/// @} diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 74258a9..af40147 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -1,7 +1,9 @@ #pragma once /// \file project.h -/// \author Folling +/// \author Folling + +#include #include #include @@ -15,10 +17,19 @@ IKARUS_BEGIN_HEADER /// \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. 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. /// \param path The path to the project. /// \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. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param flags Flags for creating the project. /// \param error_out \see errors.h /// \return The created project or null if an error occurs. -/// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use -/// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_create( - char const * path, - char const * name, - IkarusErrorData * error_out +/// \remark Must be closed with #ikarus_project_close. +IKA_API struct IkarusProject * ikarus_project_create( + char const * path, + char const * name, + IkarusProjectCreateFlags flags, + 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. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param flags Flags for creating the project. /// \param error_out \see errors.h /// \return The created project or null if an error occurs. -/// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use -/// ikarus_project_delete -IKA_API IkarusProject * -ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out); +/// \remark Must be closed with #ikarus_project_close. +IKA_API struct IkarusProject * ikarus_project_create_in_memory( + char const * name, + IkarusProjectCreateInMemoryFlags flags, + IkarusErrorData * error_out +); + +/// \brief Flags for opening a project. +enum IkarusProjectOpenFlags { + /// \brief No flags. + IkarusProjectOpenFlags_None = 0, +}; /// \brief Opens an existing project. /// \param path The path to the project. /// \pre \li Must not be null. /// \pre \li Must point to an existing project on the system. +/// \param flags Flags for opening the project. /// \param error_out \see errors.h /// \return The opened project or null if an error occurs. -/// \remark Must be freed using ikarus_free. Freeing does not delete the project from the filesystem. For that, use -/// ikarus_project_delete -IKA_API IkarusProject * -ikarus_project_open(char const * path, 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 +/// \remark Must be closed with #ikarus_project_close. +IKA_API struct IkarusProject * ikarus_project_open( + char const * path, + IkarusProjectOpenFlags flags, + IkarusErrorData * error_out ); -/// \brief Sets the name of a project. -/// \param project The project to set the name of. +/// \brief Flags for closing a project in memory. +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 exist. -/// \param new_name The new name of the project. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. +/// \param flags Flags for deleting the project. /// \param error_out \see errors.h -IKA_API void ikarus_project_set_name( - IkarusProject * project, - char const * new_name, - IkarusErrorData * error_out +/// \remark The project must not be used after deletion. +/// \remark Mutually exclusive with #ikarus_project_close. +IKA_API void ikarus_project_delete( + 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. -/// \param project The project to get the path of. +/// \param project The project to delete. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The path of the project. -/// \remark Ownership remains with libikarus, must not be freed. +/// \returns The path of the project or null if an error occurs. IKA_API char const * ikarus_project_get_path( - IkarusProject const * project, - IkarusErrorData * error_out + struct IkarusProject const * project, + IkarusErrorData * error_out ); -/// \brief Gets the entities of a project. -/// \param project The project to get the entities of. +/// \brief Gets all entities in a project. +/// \param project The project from which to get the entities. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param entities_out The buffer to write the entities to. -/// \pre \li Must not be null. -/// \param entities_out_size The size of the buffer. +/// \param size_out An out parameter for the number of entities in the returned array +/// or undefined if an error occurs. /// \param error_out \see errors.h -IKA_API void ikarus_project_get_entities( - IkarusProject * project, - struct IkarusEntity ** entities_out, - size_t entities_out_size, - IkarusErrorData * error_out +/// \returns An array of entities or null if an error occurs. +IKA_API struct IkarusEntity ** ikarus_project_get_entities( + struct IkarusProject const * project, + size_t * size_out, + IkarusErrorData * error_out ); -/// \brief Gets the number of entities of a project. -/// \param project The project to get the number of entities of. +/// \brief Gets how many entities in a project there are. +/// \param project The project from which to get the count. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The number of entities or undefined if an error occurs. -IKA_API size_t ikarus_project_get_entity_count( - IkarusProject const * project, - IkarusErrorData * error_out +/// \return The count or 0 if an error occurs. +IKA_API size_t ikarus_project_get_entities_count( + IkarusProject const * project, + IkarusErrorData * error_out ); -/// \brief Gets the blueprints of a project. -/// \param project The project to get the blueprints of. +/// \brief Gets all blueprints from a project. +/// \param project The project from which to get the blueprints. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param blueprints_out The buffer to write the blueprints to. -/// \pre \li Must not be null. -/// \param blueprints_out_size The size of the buffer. +/// \param size_out An out parameter for the number of blueprints in the returned array +/// or undefined if an error occurs. /// \param error_out \see errors.h -IKA_API void ikarus_project_get_blueprints( - IkarusProject * project, - struct IkarusBlueprint ** blueprints_out, - size_t blueprints_out_size, - IkarusErrorData * error_out +/// \return An array of blueprints or null if an error occurs. +IKA_API struct IkarusBlueprint ** ikarus_project_get_blueprints( + struct IkarusProject const * project, + size_t * size_out, + IkarusErrorData * error_out ); -/// \brief Gets the number of blueprints of a project. -/// \param project The project to get the number of blueprints of. +/// \brief Gets how many blueprints in a project there are. +/// \param project The project from which to get the count. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count( - IkarusProject const * project, - IkarusErrorData * error_out +/// \return The count or 0 if an error occurs. +IKA_API size_t ikarus_project_get_blueprints_count( + IkarusProject const * project, + IkarusErrorData * error_out ); IKARUS_END_HEADER diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h index 4274e3f..71d07c2 100644 --- a/include/ikarus/stdtypes.h +++ b/include/ikarus/stdtypes.h @@ -1,10 +1,11 @@ #pragma once #ifdef __cplusplus -#include -#include + #include + #include using std::size_t; #else -#include -#include + #include + #include + #include #endif diff --git a/include/ikarus/values/data.h b/include/ikarus/values/data.h new file mode 100644 index 0000000..d4e2367 --- /dev/null +++ b/include/ikarus/values/data.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include + +/// \file data.h +/// \author Folling + +/// \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 diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h deleted file mode 100644 index 220a11c..0000000 --- a/include/ikarus/values/number_value.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -/// \file number_value.h -/// \author Folling - -#include -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/values/schema.h b/include/ikarus/values/schema.h new file mode 100644 index 0000000..9e25e01 --- /dev/null +++ b/include/ikarus/values/schema.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include + +/// \file schema.h +/// \author Folling + +/// \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 diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h deleted file mode 100644 index 3b7d978..0000000 --- a/include/ikarus/values/text_value.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -/// \file text_value.h -/// \author Folling - -#include -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h deleted file mode 100644 index 05c5ddc..0000000 --- a/include/ikarus/values/toggle_value.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -/// \file toggle_value.h -/// \author Folling - -#include -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index aec86ac..870f551 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -1,61 +1,26 @@ #pragma once -/// \file value.h -/// \author Folling - -/// \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. 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 #include +#include + +/// \file value.h +/// \author Folling + +/// \defgroup entities Entities +/// \brief Entities are the core building blocks of Ikarus. IKARUS_BEGIN_HEADER -/// \brief The common type for all value data. -struct IkarusValue; - -/// \brief Visits an entity value, -/// calling the appropriate function for the value's type. -/// \param value The entity value to visit. -/// \pre \li Must not be null. -/// \param toggle_visitor The function to call if the value is a toggle value. -/// \remark Skipped if null. -/// \param number_visitor The function to call if the value is a number value. -/// \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 -); +/// \brief Values are data containers for entities. +/// \details Values are flexible enough to store any kind of data. They are +/// akin to objects in programming languages. +/// Each value has a schema that defines the type of the value. \see schema.h +/// They also store data appropriate for the schema. +/// +/// Given the complexity of values, they are transferred as json. +/// The json representation of a value is a map with the following keys: +/// - `schema`: The schema of the value. \see schema.h. +/// - `data`: The data of the value. \see data.h. IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/values/value_cardinality.h b/include/ikarus/values/value_cardinality.h deleted file mode 100644 index 6d5526f..0000000 --- a/include/ikarus/values/value_cardinality.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -/// \file value_cardinality.h -/// \author Folling - -/// \addtogroup values Values -/// @{ - -#include - -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 - -/// @} diff --git a/include/ikarus/values/value_type.h b/include/ikarus/values/value_type.h deleted file mode 100644 index caa6165..0000000 --- a/include/ikarus/values/value_type.h +++ /dev/null @@ -1,198 +0,0 @@ -#pragma once - -/// \file value_schema.h -/// \author Folling - -/// \addtogroup values Values -/// @{ - -#include -#include -#include - -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, Number, Complex<"Foo":Toggle,"Bar":Text> -struct IkarusValueLayout; -/// \brief A fixed datapoint with a fixed layout. -/// Example: Age: Union> -struct IkarusValueLayoutConstant; -/// \brief A singular datapoint with one of a list of layouts. -/// Example: ChestReward: Combination -struct IkarusValueLayoutCombination; -/// \brief A collection of datapoints with a homogenous layout. -/// Example: Friends: List -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 - -/// @} diff --git a/include/module.modulemap b/include/module.modulemap deleted file mode 100644 index 6b2dbcb..0000000 --- a/include/module.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -module ikarus { - header "ikarus/persistence/project.h" - header "ikarus/objects/entity.h" - header "ikarus/objects/blueprint.h" - export * -} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0963d2e..b928e93 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,4 +7,4 @@ file( "*.c" ) -set(SOURCE_FILES ${FILES} PARENT_SCOPE) \ No newline at end of file +set(SOURCE_FILES ${FILES} PARENT_SCOPE) diff --git a/src/ikarus/errors.cpp b/src/ikarus/errors.cpp index 8f8ff3c..b3e4ae2 100644 --- a/src/ikarus/errors.cpp +++ b/src/ikarus/errors.cpp @@ -1,41 +1,72 @@ #include "ikarus/errors.h" +#include + +#include + +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) { - switch (info) { - case IkarusErrorInfo_None: return "None"; + switch (info) { + case IkarusErrorInfo_None: return "None"; - case IkarusErrorInfo_Client_Misuse: return "Client::Misuse"; - case IkarusErrorInfo_Client_InvalidInput: return "Client::InvalidInput"; - case IkarusErrorInfo_Client_InvalidFormat: return "Client::InvalidFormat"; - case IkarusErrorInfo_Client_ConstraintViolated: return "Client::ConstraintViolated"; + case IkarusErrorInfo_Client_Misuse: return "Client::Misuse"; + case IkarusErrorInfo_Client_InvalidInput: return "Client::InvalidInput"; + case IkarusErrorInfo_Client_NonExistent: return "Client::NonExistent"; + case IkarusErrorInfo_Client_InvalidFormat: return "Client::InvalidFormat"; + case IkarusErrorInfo_Client_ConstraintViolated: + return "Client::ConstraintViolated"; - case IkarusErrorInfo_Filesystem_NotFound: return "Filesystem::NotFound"; - case IkarusErrorInfo_Filesystem_AlreadyExists: return "Filesystem::AlreadyExists"; - case IkarusErrorInfo_Filesystem_MissingPermissions: return "Filesystem::MissingPermissions"; - case IkarusErrorInfo_Filesystem_InsufficientSpace: return "Filesystem::InsufficientSpace"; - case IkarusErrorInfo_Filesystem_InvalidPath: return "Filesystem::InvalidPath"; + case IkarusErrorInfo_Filesystem_NotFound: return "Filesystem::NotFound"; + case IkarusErrorInfo_Filesystem_AlreadyExists: + return "Filesystem::AlreadyExists"; + case IkarusErrorInfo_Filesystem_MissingPermissions: + 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_QueryFailed: return "Database::QueryFailed"; - case IkarusErrorInfo_Database_MigrationFailed: return "Database::MigrationFailed"; - case IkarusErrorInfo_Database_InvalidState: return "Database::InvalidState"; + case IkarusErrorInfo_Database_ConnectionFailed: + return "Database::ConnectionFailed"; + case IkarusErrorInfo_Database_QueryFailed: return "Database::QueryFailed"; + case IkarusErrorInfo_Database_MigrationFailed: + return "Database::MigrationFailed"; + case IkarusErrorInfo_Database_InvalidState: return "Database::InvalidState"; - case IkarusErrorInfo_OS_SystemCallFailed: return "OS::SystemCallFailed"; - case IkarusErrorInfo_OS_InvalidReturnValue: return "OS::InvalidReturnValue"; - case IkarusErrorInfo_OS_InsufficientMemory: return "OS::InsufficientMemory"; + case IkarusErrorInfo_OS_SystemCallFailed: return "OS::SystemCallFailed"; + case IkarusErrorInfo_OS_InvalidReturnValue: return "OS::InvalidReturnValue"; + case IkarusErrorInfo_OS_InsufficientMemory: return "OS::InsufficientMemory"; - case IkarusErrorInfo_LibIkarus_InvalidState: return "LibIkarus::InvalidState"; - case IkarusErrorInfo_LibIkarus_CannotPerformOperation: return "LibIkarus::CannotPerformOperation"; - case IkarusErrorInfo_LibIkarus_Timeout: return "LibIkarus::Timeout"; + case IkarusErrorInfo_LibIkarus_InvalidState: + return "LibIkarus::InvalidState"; + 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) { - return data->info == IkarusErrorInfo_None; + return data->info == IkarusErrorInfo_None; } bool ikarus_error_data_is_error(IkarusErrorData const * data) { - return data->info != IkarusErrorInfo_None; + return data->info != IkarusErrorInfo_None; } diff --git a/src/ikarus/errors.hpp b/src/ikarus/errors.hpp index e12a68f..55b3e8f 100644 --- a/src/ikarus/errors.hpp +++ b/src/ikarus/errors.hpp @@ -1,80 +1,169 @@ #pragma once #include +#include #include -inline void safe_strcpy(char * dest, std::string_view src, size_t dest_size) { - for (int i = 0; i < dest_size; ++i) { - if (src[i] == '\0') { - dest[i] = '\0'; - return; - } +void safe_strcpy( + std::string_view const src, + char * dest, + size_t const dest_size +); - dest[i] = src[i]; - } -} - -#define IKARUS_SET_ERROR(msg, err_info) \ - if (error_out != nullptr) { \ - safe_strcpy(static_cast(error_out->message), msg, IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT); \ - error_out->info = err_info; \ - } +#define IKARUS_SET_ERROR(msg, err_info) \ + if (error_out != nullptr) { \ + safe_strcpy( \ + msg, \ + static_cast(error_out->message), \ + IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT \ + ); \ + error_out->info = err_info; \ + } #define IKARUS_FAIL(ret, msg, err_info) \ - IKARUS_SET_ERROR(msg, err_info); \ - return ret + IKARUS_SET_ERROR(msg, err_info); \ + return ret #define IKARUS_FAIL_IF(condition, ret, msg, err_info) \ - if (condition) { \ - IKARUS_SET_ERROR(msg, err_info) \ - return ret; \ - } + if (condition) { \ + IKARUS_SET_ERROR(msg, err_info) \ + return ret; \ + } -#define IKARUS_FAIL_IF_ERROR(ret) \ - if (ikarus_error_data_is_error(error_out)) { \ - return ret; \ - } +#define IKARUS_TRY_OR_FAIL_IMPL(var_name, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + 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, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ - return var_name; \ - } - -#define IKARUS_TRY_OR_FAIL(msg, err_info, ...) IKARUS_TRY_OR_FAIL_IMPL(CPPBASE_UNIQUE_NAME(result), msg, err_info, __VA_ARGS__); - -#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ - return ret; \ - } +#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR( \ + fmt::format( \ + fmt::runtime(msg), \ + std::move(var_name).unwrap_error() \ + ), \ + err_info \ + ); \ + return ret; \ + } #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, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ - return var_name; \ - } \ - value = var_name.unwrap_value() +#define IKARUS_VTRY_OR_FAIL_IMPL(var_name, value, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR( \ + fmt::format( \ + fmt::runtime(msg), \ + 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, ...) \ - 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, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ - return ret; \ - } \ - value = var_name.unwrap_value() +#define IKARUS_VTRYRV_OR_FAIL_IMPL(var_name, value, ret, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR( \ + fmt::format(fmt::runtime(msg), var_name.unwrap_error()), \ + err_info \ + ); \ + return ret; \ + } \ + value = std::move(var_name).unwrap_value() #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::object_name \ + ), \ + IkarusErrorInfo_Database_QueryFailed, \ + object->project->db->query_one( \ + fmt::format( \ + "SELECT EXISTS(SELECT 1 FROM `{}` WHERE `id` = ?)", \ + std::remove_cvref_t::table_name \ + ), \ + object->id \ + ) \ + ); \ + \ + IKARUS_FAIL_IF( \ + !exists, \ + ret, \ + fmt::format( \ + "{} doesn't exist", \ + std::remove_cvref_t::object_name \ + ), \ + IkarusErrorInfo_Client_NonExistent \ + ) diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index a2a5673..c25961f 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -1,168 +1,5 @@ #include "ikarus/objects/blueprint.h" -#include -#include -#include - #include #include -#include -#include -#include -#include #include -#include - -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 { - 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 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( - "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("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( - "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("SELECT COUNT(`entity`) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) - ); - - return ret; -} diff --git a/src/ikarus/objects/blueprint.hpp b/src/ikarus/objects/blueprint.hpp index cd46d62..b56143f 100644 --- a/src/ikarus/objects/blueprint.hpp +++ b/src/ikarus/objects/blueprint.hpp @@ -1,19 +1,23 @@ #pragma once -#include +#include -struct IkarusBlueprint : public IkarusObject { -public: - IkarusBlueprint(struct IkarusProject * project, int64_t id); +struct IkarusBlueprint { + consteval static inline auto OBJECT_NAME() -> std::string_view { + return "blueprint"; + } - IkarusBlueprint(IkarusBlueprint const &) = default; - IkarusBlueprint(IkarusBlueprint &&) = default; + consteval static inline auto TABLE_NAME() -> std::string_view { + return "blueprints"; + } - IkarusBlueprint & operator=(IkarusBlueprint const &) = default; - IkarusBlueprint & operator=(IkarusBlueprint &&) = default; + IkarusBlueprint( + struct IkarusProject * project, + int64_t id, + std::string_view name + ); - ~IkarusBlueprint() override = default; - -public: - std::string_view get_table_name() const noexcept override; + struct IkarusProject * project; + int64_t id; + std::string name; }; diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 2df1404..47f1f90 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -1,488 +1,171 @@ -#include "ikarus/objects/entity.h" - -#include +#include "entity.hpp" +#include #include -#include -#include -#include -#include +#include #include -#include -#include -#include -IkarusEntity::IkarusEntity(IkarusProject * project, int64_t id): - IkarusObject{project, id} {} - -std::string_view IkarusEntity::get_table_name() const noexcept { - return "entities"; -} +IkarusEntity::IkarusEntity( + struct IkarusProject * project, + int64_t id, + std::string_view name +): + project{project}, + id{id}, + name{name} {} IkarusEntity * ikarus_entity_create( - struct IkarusProject * project, - char const * name, - IkarusErrorData * error_out + struct IkarusProject * project, + char const * name, + IkarusEntityCreateFlags flags, + IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); - IKARUS_VTRYRV_OR_FAIL( - int64_t const id, - nullptr, - "failed to create entity: {}", - IkarusErrorInfo_Database_QueryFailed, - project->db->transact( - [name](auto * db - ) -> cppbase::Result { - TRY(db->execute( - "INSERT INTO `entities`(`name`) VALUES(?, ?)", - name - )); - return cppbase::ok(db->last_insert_rowid()); - } - ) - ); + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to create entity: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->execute("INSERT INTO `entities`(`name`) VALUES(?)", name) + ); - 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) { - IKARUS_FAIL_IF_NULL(entity, ); - IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); - - 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 +void ikarus_entity_delete( + IkarusEntity * entity, + IkarusEntityDeleteFlags flags, + 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 const * entity, - IkarusErrorData * error_out +IkarusEntity * ikarus_entity_copy( + IkarusEntity * entity, + 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 { + 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( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out + IkarusEntity * entity, + char const * name, + 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( - IkarusEntity const * entity, - struct IkarusBlueprint const * blueprint, - IkarusErrorData * error_out +char const * ikarus_entity_get_value( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, false); - IKARUS_FAIL_IF_OBJECT_MISSING(entity, false); - 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( - "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( - "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( - "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( - "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; + IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); } void ikarus_entity_set_value( - IkarusEntity * entity, - char const * name, - struct IkarusValue const * value, - IkarusErrorData * error_out + IkarusEntity * entity, + char const * name, + char const * value, + IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, ); - IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); - IKARUS_FAIL_IF_NAME_INVALID(name, ); - 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( - "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 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( - "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( - "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()) - ) - ); + IKARUS_FAIL_IF_NULL(entity, ); + IKARUS_FAIL_IF_NULL(name, ); + IKARUS_FAIL_IF_NULL(value, ); } diff --git a/src/ikarus/objects/entity.hpp b/src/ikarus/objects/entity.hpp index e3bc035..90a445e 100644 --- a/src/ikarus/objects/entity.hpp +++ b/src/ikarus/objects/entity.hpp @@ -1,19 +1,24 @@ #pragma once -#include +#include +#include -struct IkarusEntity : public IkarusObject { -public: - inline IkarusEntity(struct IkarusProject * project, int64_t id); +#include - IkarusEntity(IkarusEntity const &) = default; - IkarusEntity(IkarusEntity &&) = default; +struct IkarusEntity { + constinit static inline auto object_name = "entity"; + constinit static inline auto table_name = "entities"; - IkarusEntity & operator=(IkarusEntity const &) = default; - IkarusEntity & operator=(IkarusEntity &&) = default; + IkarusEntity( + struct IkarusProject * project, + int64_t id, + std::string_view name + ); - ~IkarusEntity() override = default; + struct IkarusProject * project; + int64_t id; + std::string name; -public: - std::string_view get_table_name() const noexcept override; + std::vector> values_ordered; + std::unordered_map values; }; diff --git a/src/ikarus/objects/object.cpp b/src/ikarus/objects/object.cpp deleted file mode 100644 index 01747cc..0000000 --- a/src/ikarus/objects/object.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "object.hpp" - -#include - -IkarusObject::IkarusObject(IkarusProject * project, int64_t id): - project{project}, - id{id} {} diff --git a/src/ikarus/objects/object.hpp b/src/ikarus/objects/object.hpp deleted file mode 100644 index 96d5911..0000000 --- a/src/ikarus/objects/object.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -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("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); diff --git a/src/ikarus/objects/properties/number_property.cpp b/src/ikarus/objects/properties/number_property.cpp deleted file mode 100644 index 60c9ccd..0000000 --- a/src/ikarus/objects/properties/number_property.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "number_property.hpp" - -#include - -#include -#include -#include -#include - -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(project, name, property_source, error_out); -} - -IkarusNumberValue * ikarus_number_property_get_default_value(IkarusNumberProperty * property, IkarusErrorData * error_out) { - return ikarus::util::get_default_value(property, error_out); -} - -void ikarus_number_property_set_default_value( - IkarusNumberProperty * property, - IkarusNumberValue * new_default_value, - IkarusErrorData * error_out -) { - ikarus::util::set_default_value(property, new_default_value, error_out); -} diff --git a/src/ikarus/objects/properties/number_property.hpp b/src/ikarus/objects/properties/number_property.hpp deleted file mode 100644 index 55ff25c..0000000 --- a/src/ikarus/objects/properties/number_property.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include -#include - -struct IkarusNumberProperty : public IkarusProperty { -public: - using value_type = IkarusNumberValue; - constexpr auto static PropertyType = IkarusValueType_Number; - -public: - IkarusNumberProperty(struct IkarusProject * project, int64_t id); -}; diff --git a/src/ikarus/objects/properties/property.cpp b/src/ikarus/objects/properties/property.cpp deleted file mode 100644 index ecc2f3e..0000000 --- a/src/ikarus/objects/properties/property.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "property.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include - -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("SELECT `type` FROM `properties` WHERE `id` = ?", property->id) - ); - - return static_cast(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("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(property); -} - -IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property) { - return static_cast(property); -} diff --git a/src/ikarus/objects/properties/property.hpp b/src/ikarus/objects/properties/property.hpp deleted file mode 100644 index 1fd0394..0000000 --- a/src/ikarus/objects/properties/property.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include - -struct IkarusProperty : public IkarusObject { -public: - using Data = std::variant; - -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; -}; diff --git a/src/ikarus/objects/properties/text_property.cpp b/src/ikarus/objects/properties/text_property.cpp deleted file mode 100644 index c51d653..0000000 --- a/src/ikarus/objects/properties/text_property.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "text_property.hpp" - -#include - -#include -#include -#include -#include - -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(project, name, property_source, error_out); -} - -IkarusTextValue * ikarus_text_property_get_default_value(IkarusTextProperty * property, IkarusErrorData * error_out) { - return ikarus::util::get_default_value(property, error_out); -} - -void ikarus_text_property_set_default_value( - IkarusTextProperty * property, - IkarusTextValue * new_default_value, - IkarusErrorData * error_out -) { - ikarus::util::set_default_value(property, new_default_value, error_out); -} diff --git a/src/ikarus/objects/properties/text_property.hpp b/src/ikarus/objects/properties/text_property.hpp deleted file mode 100644 index b690cda..0000000 --- a/src/ikarus/objects/properties/text_property.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include -#include - -struct IkarusTextProperty : public IkarusProperty { -public: - using value_type = IkarusTextValue; - constexpr auto static PropertyType = IkarusValueType_Text; - -public: - IkarusTextProperty(struct IkarusProject * project, int64_t id); -}; diff --git a/src/ikarus/objects/properties/toggle_property.cpp b/src/ikarus/objects/properties/toggle_property.cpp deleted file mode 100644 index d1705a2..0000000 --- a/src/ikarus/objects/properties/toggle_property.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "toggle_property.hpp" - -#include - -#include -#include -#include -#include - -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(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(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(property, new_default_value, error_out); -} diff --git a/src/ikarus/objects/properties/toggle_property.hpp b/src/ikarus/objects/properties/toggle_property.hpp deleted file mode 100644 index 35575ee..0000000 --- a/src/ikarus/objects/properties/toggle_property.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include -#include - -struct IkarusToggleProperty { -public: - using value_type = IkarusToggleValue; - constexpr auto static PropertyType = IkarusValueType_Toggle; - -public: - IkarusToggleProperty(struct IkarusProject * project, int64_t id); - -public: - IkarusProperty * property; -}; diff --git a/src/ikarus/objects/properties/util.hpp b/src/ikarus/objects/properties/util.hpp deleted file mode 100644 index 97146f1..0000000 --- a/src/ikarus/objects/properties/util.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace ikarus::util { -template -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 { - 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(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().pretty_name()), - IkarusErrorInfo_LibIkarus_InvalidState - ); - - return ret; -} - -template -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(&value->data); - - IKARUS_FAIL_IF( - ret == nullptr, - nullptr, - fmt::format( - "{} default value is not a(n) {}", - boost::typeindex::type_id().pretty_name(), - boost::typeindex::type_id().pretty_name() - ), - IkarusErrorInfo_Database_InvalidState - ); - - return *ret; -} - -template -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 diff --git a/src/ikarus/objects/property.cpp b/src/ikarus/objects/property.cpp new file mode 100644 index 0000000..d096b18 --- /dev/null +++ b/src/ikarus/objects/property.cpp @@ -0,0 +1,15 @@ +#include "property.hpp" + +#include +#include +#include +#include + +IkarusProperty::IkarusProperty( + struct IkarusProject * project, + int64_t id, + std::string_view name +): + project{project}, + id{id}, + name{name} {} diff --git a/src/ikarus/objects/property.hpp b/src/ikarus/objects/property.hpp new file mode 100644 index 0000000..29143fb --- /dev/null +++ b/src/ikarus/objects/property.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +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; +}; diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp deleted file mode 100644 index e625f5d..0000000 --- a/src/ikarus/objects/util.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include -#include - -namespace ikarus::util { - -template -[[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 -[[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 -[[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(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 -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 diff --git a/src/ikarus/persistence/migrations.hpp b/src/ikarus/persistence/migrations.hpp index db649a5..a3a1d5e 100644 --- a/src/ikarus/persistence/migrations.hpp +++ b/src/ikarus/persistence/migrations.hpp @@ -1,7 +1,5 @@ #pragma once -#include - #include #include @@ -9,7 +7,10 @@ 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 { public: @@ -27,7 +28,11 @@ public: std::string_view sql; }; -#define DECLARE_MIGRATION(name) std::make_unique(static_cast(name()), name##_size()) +#define DECLARE_MIGRATION(name) \ + std::make_unique( \ + static_cast(name()), \ + name##_size() \ + ) constexpr std::string_view DB_VERSION_KEY = "IKARUS_DB_VERSION"; std::vector> const MIGRATIONS = []() { diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index aa28c8a..8b263d7 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -6,13 +6,14 @@ CREATE TABLE `entities` CREATE TABLE `entity_values` ( + `id` INTEGER PRIMARY KEY, `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `name` TEXT NOT NULL, - `type` INT NOT NULL, `value` TEXT NOT NULL, - PRIMARY KEY (`entity`, `name`) -) WITHOUT ROWID, STRICT; + PRIMARY KEY (`id`), + UNIQUE (`entity`, `name`) +) STRICT; CREATE TABLE `blueprints` ( @@ -25,14 +26,11 @@ CREATE TABLE `properties` `id` INTEGER PRIMARY KEY, `blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE, `name` TEXT NOT NULL, - `type` INT NOT NULL, - `cardinality` INT NOT NULL, + `schema` TEXT NOT NULL, `default_value` TEXT NOT NULL, `settings` TEXT NOT NULL ) STRICT; -CREATE INDEX `properties_type` ON `properties` (`type`); - CREATE TABLE `entity_blueprint_links` ( `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, @@ -43,9 +41,11 @@ CREATE TABLE `entity_blueprint_links` CREATE TABLE `entity_property_values` ( + `id` INTEGER PRIMARY KEY, `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `property` INTEGER NOT NULL REFERENCES `properties` (`id`) ON DELETE CASCADE, `value` TEXT NOT NULL, - PRIMARY KEY (`entity`, `property`) -) WITHOUT ROWID, STRICT; + PRIMARY KEY (`id`), + UNIQUE (`entity`, `property`) +) STRICT; diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 0d5b1e5..300bf86 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -1,339 +1,348 @@ #include "ikarus/persistence/project.h" +#include + #include +#include #include +#include +#include +#include #include #include -#include -#include -#include -#include -#include #include #include -IkarusProject::IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db): - name{name}, - path{std::move(path)}, - db{std::move(db)}, - _blueprints{}, - _properties{}, - _entities{} {} +constexpr char const * DB_PROJECT_ID_KEY = "project_id"; +constexpr char const * DB_PROJECT_NAME_KEY = "project_name"; -IkarusBlueprint * IkarusProject::get_blueprint(int64_t id) { - return get_cached_object(id, this->_blueprints); +auto create_impl( + std::string_view path, + std::string_view name, + IkarusErrorData * error_out +) -> std::unique_ptr { + 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 { - remove_cached_object(blueprint, _blueprints); -} - -auto IkarusProject::get_entity(int64_t id) -> IkarusEntity * { - return get_cached_object(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(this, id)).first->second.get(); - case IkarusValueType_Number: - return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); - case IkarusValueType_Text: return _properties.emplace(id, std::make_unique(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("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 +IkarusProject * ikarus_project_create( + char const * path, + char const * name, + IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, ); - IKARUS_FAIL_IF_NULL(blueprints_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 + ); - if (blueprints_out_size == 0) { - return; - } + boost::filesystem::path fs_path{path}; - 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( - , - "unable to fetch project blueprints from database: {}", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered("SELECT `id` FROM `blueprints`", ids, blueprints_out_size) - ); + if (auto db = create_impl(fs_path.c_str(), name, error_out); !db) { + LOG_WARN("creating project failed, removing file at {}", path); - for (size_t i = 0; i < blueprints_out_size; ++i) { - blueprints_out[i] = project->get_blueprint(ids[i]); - } + boost::system::error_code ec; + 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) { - IKARUS_FAIL_IF_NULL(project, 0); +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 const ret, - 0, - "unable to fetch project blueprint count from database: {}", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_one("SELECT COUNT(*) FROM `blueprints`") - ); + if (auto db = create_impl(":memory:", name, error_out); !db) { + return nullptr; + } else { + IKARUS_TRYRV_OR_FAIL( + nullptr, + "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( + "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( - IkarusProject * project, - struct IkarusEntity ** entities_out, - size_t entities_out_size, - IkarusErrorData * error_out + struct IkarusProject const * project, + int64_t * ids_out, + uint64_t ids_out_size, + IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, ); - IKARUS_FAIL_IF_NULL(entities_out, ); + IKARUS_FAIL_IF_NULL(project, ); + IKARUS_FAIL_IF_NULL(ids_out, ); - if (entities_out_size == 0) { - return; - } + if (ids_out == 0) { + return; + } - int64_t ids[entities_out_size]; - - IKARUS_TRYRV_OR_FAIL( - , - "unable to fetch project entities from database: {}", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered("SELECT `id` FROM `entities`", ids, entities_out_size) - ); - - for (size_t i = 0; i < entities_out_size; ++i) { - entities_out[i] = project->get_entity(ids[i]); - } + IKARUS_TRYRV_OR_FAIL( + , + "unable to fetch project entities from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_many_buffered( + "SELECT `id` FROM `entities`", + ids_out, + ids_out_size + ) + ); } -size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(project, 0); +size_t ikarus_project_get_entity_count( + IkarusProject const * project, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(project, 0); - IKARUS_VTRYRV_OR_FAIL( - auto const ret, - 0, - "unable to fetch project entity count from database: {}", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_one("SELECT COUNT(*) FROM `entities`") - ); + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + 0, + "unable to fetch project entity count from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT COUNT(`id`) FROM `entities`") + ); - return ret; + return ret; } -struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NULL(name, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); +void ikarus_project_get_blueprints( + struct IkarusProject const * project, + int64_t * ids_out, + 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 - // errors. In this case `sqlitecpp::MissingRow` and database related errors. Same for the other functions. - IKARUS_VTRYRV_OR_FAIL( - auto const id, - nullptr, - "unable to find entity in database: {}", - IkarusErrorInfo_Client_InvalidInput, - project->db->query_one("SELECT `id` FROM `entities` WHERE `name` = ?", name) - ); + if (ids_out == 0) { + return; + } - return project->get_entity(id); + IKARUS_TRYRV_OR_FAIL( + , + "unable to fetch project blueprints from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_many_buffered( + "SELECT `id` FROM `blueprints`", + ids_out, + ids_out_size + ) + ); } -struct IkarusProperty * -get_property_by_name(IkarusProject * project, IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NULL(name, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); +size_t ikarus_project_get_blueprint_count( + IkarusProject const * project, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(project, 0); - IKARUS_VTRYRV_OR_FAIL( - auto const id_and_type, - nullptr, - "unable to find property in database: {}", - IkarusErrorInfo_Client_InvalidInput, - project->db->query_one( - "SELECT `id`, `type` FROM `properties` WHERE `name` = ? AND `scope` = ?", - name, - scope->get_id() - ) - ); + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + 0, + "unable to fetch project blueprint count from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT COUNT(`id`) FROM `blueprints`") + ); - auto const [id, type] = id_and_type; - - return project->get_property(id, type); -} - -IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NULL(name, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); - - IKARUS_VTRYRV_OR_FAIL( - auto const id, - nullptr, - "unable to find blueprint in database: {}", - IkarusErrorInfo_Client_InvalidInput, - project->db->query_one("SELECT `id` FROM `blueprints` WHERE `name` = ?", name) - ); - - return project->get_blueprint(id); + return ret; } diff --git a/src/ikarus/persistence/project.hpp b/src/ikarus/persistence/project.hpp index 1dc69dc..f202ea2 100644 --- a/src/ikarus/persistence/project.hpp +++ b/src/ikarus/persistence/project.hpp @@ -1,58 +1,19 @@ #pragma once +#include +#include #include #include #include -#include -#include +#include +#include +#include -namespace fs = boost::filesystem; - -/// \private struct IkarusProject { -public: - IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && 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 - [[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(this, id)).first->second.get(); - } - - return iter->second.get(); - } - - template - void remove_cached_object(T * object, std::unordered_map> & cache) { - cache.erase(object->id); - } - -public: - std::string name; - std::string_view path; - std::unique_ptr db; - -private: - std::unordered_map> mutable _blueprints; - std::unordered_map> mutable _properties; - std::unordered_map> mutable _entities; + std::string name; + boost::filesystem::path path; + std::unique_ptr db; }; - -constexpr std::string_view DB_PROJECT_NAME_KEY = "PROJECT_NAME"; diff --git a/src/ikarus/values/data.cpp b/src/ikarus/values/data.cpp new file mode 100644 index 0000000..ac893b2 --- /dev/null +++ b/src/ikarus/values/data.cpp @@ -0,0 +1,217 @@ +#include "data.hpp" + +#include + +#include +#include + +#include +#include +#include +#include + +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 { + if (!json.is_object()) { + return cppbase::err(IkarusJsonError{IkarusJsonInvalidTypeError{}}); + } + + IkarusValueData value{}; + + VTRY( + auto type, + deserialize_enum( + json, + "type", + IkarusValueDataType_Primitive, + IkarusValueDataType_Tuple + ) + ); + + switch (type) { + case IkarusValueDataType_Primitive: { + VTRY( + auto primitive, + deserialize_enum( + json, + "primitive", + IkarusValuePrimitiveType_Toggle, + IkarusValuePrimitiveType_Text + ) + ); + + switch (primitive) { + case IkarusValuePrimitiveType_Toggle: { + VTRY(auto data, deserialize_any(json, "data")); + + value.variant = IkarusValueDataPrimitiveToggle{data}; + break; + } + case IkarusValuePrimitiveType_Number: { + VTRY(auto data, deserialize_any(json, "data")); + + value.variant = IkarusValueDataPrimitiveNumber{data}; + break; + } + case IkarusValuePrimitiveType_Text: { + VTRY(auto data, deserialize_any(json, "data")); + + value.variant = IkarusValueDataPrimitiveString{data}; + break; + } + } + + break; + } + case IkarusValueDataType_List: { + std::vector> values_data{}; + VTRY( + auto data_json, + deserialize_any>(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(std::move(value_data)) + ); + } + + value.variant = IkarusValueDataList{values_data}; + break; + } + case IkarusValueDataType_Map: { + std::vector, + cppbase::owning_ptr>> + map_data{}; + + VTRY( + auto map_data_json, + deserialize_any>(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(key), + cppbase::make_owning(value) + ); + } + + value.variant = IkarusValueDataMap{map_data}; + break; + } + case IkarusValueDataType_Tuple: { + std::vector> values_data{}; + + VTRY( + auto values_json, + deserialize_any>(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(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>(); + }, + [&](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>(); + }, + [&](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>(); + } + }, + value.variant + ); +} diff --git a/src/ikarus/values/data.hpp b/src/ikarus/values/data.hpp new file mode 100644 index 0000000..d4e92ad --- /dev/null +++ b/src/ikarus/values/data.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +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> values; +}; + +struct IkarusValueDataMap { + std::vector, + cppbase::owning_ptr>> + values; +}; + +struct IkarusValueDataTuple { + std::vector> values; +}; + +struct IkarusValueData { + using IkarusValueDataVariant = std::variant< + IkarusValueDataPrimitive, + IkarusValueDataList, + IkarusValueDataMap, + IkarusValueDataTuple>; + + static auto from_json(nlohmann::json const & json) + -> cppbase::Result; + static auto to_json(nlohmann::json & json, IkarusValueData const & value) + -> void; + + IkarusValueDataVariant variant; +}; + +auto get_primitive_type(IkarusValueDataPrimitive const & primitive) + -> IkarusValuePrimitiveType; diff --git a/src/ikarus/values/entity_property_value.cpp b/src/ikarus/values/entity_property_value.cpp deleted file mode 100644 index 165383d..0000000 --- a/src/ikarus/values/entity_property_value.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "entity_property_value.hpp" - -#include - -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; -} diff --git a/src/ikarus/values/entity_property_value.hpp b/src/ikarus/values/entity_property_value.hpp deleted file mode 100644 index 6f70f09..0000000 --- a/src/ikarus/values/entity_property_value.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/src/ikarus/values/errors.hpp b/src/ikarus/values/errors.hpp new file mode 100644 index 0000000..ed752af --- /dev/null +++ b/src/ikarus/values/errors.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +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; diff --git a/src/ikarus/values/number_value.cpp b/src/ikarus/values/number_value.cpp deleted file mode 100644 index 41fba42..0000000 --- a/src/ikarus/values/number_value.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "ikarus/values/number_value.h" - -#include - -#include -#include - -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); -} diff --git a/src/ikarus/values/number_value.hpp b/src/ikarus/values/number_value.hpp deleted file mode 100644 index bd4bec5..0000000 --- a/src/ikarus/values/number_value.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include - -#include - -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> data{}; -}; diff --git a/src/ikarus/values/schema.cpp b/src/ikarus/values/schema.cpp new file mode 100644 index 0000000..e6cbd24 --- /dev/null +++ b/src/ikarus/values/schema.cpp @@ -0,0 +1,177 @@ +#include "schema.hpp" + +#include + +#include +#include +#include + +auto IkarusValueSchema::from_json(nlohmann::json const & json) + -> cppbase::Result { + if (!json.is_object()) { + return cppbase::err( + IkarusValueSchemaParseError{ + IkarusJsonError{IkarusJsonInvalidTypeError{}} + } + ); + } + + IkarusValueSchema schema{}; + + VTRY( + auto type, + deserialize_enum( + json, + "type", + IkarusValueSchemaType_Primitive, + IkarusValueSchemaType_Tuple + ) + ); + + switch (type) { + case IkarusValueSchemaType_Primitive: { + VTRY( + auto primitive, + deserialize_enum( + 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(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(std::move(key_schema)), + cppbase::make_owning(std::move(value_schema)) + }; + break; + } + case IkarusValueSchemaType_Tuple: { + VTRY( + auto sub_schemas_json, + deserialize_any>(json, "schemas") + ); + + std::vector> 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(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 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 + ); +} diff --git a/src/ikarus/values/schema.hpp b/src/ikarus/values/schema.hpp new file mode 100644 index 0000000..4f4256c --- /dev/null +++ b/src/ikarus/values/schema.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +struct IkarusValue; +struct IkarusValueData; +struct IkarusValueSchema; + +struct IkarusValueSchemaPrimitive { + IkarusValuePrimitiveType type; +}; + +struct IkarusValueSchemaList { + cppbase::owning_ptr sub_schema; +}; + +struct IkarusValueSchemaMap { + cppbase::owning_ptr key_schema; + cppbase::owning_ptr value_schema; +}; + +struct IkarusValueSchemaTuple { + std::vector> sub_schemas; +}; + +struct IkarusValueSchema { + using IkarusValueSchemaVariant = std::variant< + IkarusValueSchemaPrimitive, + IkarusValueSchemaList, + IkarusValueSchemaMap, + IkarusValueSchemaTuple>; + + static auto from_json(nlohmann::json const & json) + -> cppbase::Result; + static auto to_json(nlohmann::json & json, IkarusValueSchema const & value) + -> void; + + auto validate(IkarusValueData const & data) const -> bool; + + IkarusValueSchemaVariant variant; +}; diff --git a/src/ikarus/values/shared.hpp b/src/ikarus/values/shared.hpp new file mode 100644 index 0000000..a4d50ac --- /dev/null +++ b/src/ikarus/values/shared.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "errors.hpp" + +#include + +#include +#include + +inline auto get_key(nlohmann::json const & json, std::string_view key) + -> cppbase:: + Result { + auto iter = json.find(key); + if (iter == json.end()) { + return cppbase::err(IkarusJsonMissingKeyError{}); + } + + return cppbase::ok(iter); +} + +template +auto serialize_enum(nlohmann::json & json, std::string_view key, E value) + -> void { + json[key] = static_cast>(value); +} + +template + requires std::is_integral_v> +auto deserialize_enum( + nlohmann::json const & json, + std::string_view key, + E min, + E max +) -> cppbase::Result { + VTRY(auto iter, get_key(json, key)); + + if (!iter->is_number_integer()) { + return cppbase::err(IkarusJsonError{}); + } + + auto value = iter->get>(); + + if (value < static_cast>(min) || + value > static_cast>(max)) { + return cppbase::err(IkarusJsonError{}); + } + + return cppbase::ok(static_cast(value)); +} + +template +auto deserialize_any(nlohmann::json const & json, std::string_view key) + -> cppbase::Result { + VTRY(auto iter, get_key(json, key)); + + try { + return cppbase::ok(iter->get()); + } 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{}); + } +} diff --git a/src/ikarus/values/text_value.cpp b/src/ikarus/values/text_value.cpp deleted file mode 100644 index 9400f2e..0000000 --- a/src/ikarus/values/text_value.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "ikarus/values/text_value.h" - -#include -#include - -#include -#include - -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); -} diff --git a/src/ikarus/values/text_value.hpp b/src/ikarus/values/text_value.hpp deleted file mode 100644 index a1e36ea..0000000 --- a/src/ikarus/values/text_value.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include - -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> data{}; -}; diff --git a/src/ikarus/values/toggle_value.cpp b/src/ikarus/values/toggle_value.cpp deleted file mode 100644 index fb76e68..0000000 --- a/src/ikarus/values/toggle_value.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "ikarus/values/toggle_value.h" - -#include -#include - -#include -#include - -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); -} diff --git a/src/ikarus/values/toggle_value.hpp b/src/ikarus/values/toggle_value.hpp deleted file mode 100644 index fade2c1..0000000 --- a/src/ikarus/values/toggle_value.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include - -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> data{}; -}; diff --git a/src/ikarus/values/value.cpp b/src/ikarus/values/value.cpp index de57e26..07c0c24 100644 --- a/src/ikarus/values/value.cpp +++ b/src/ikarus/values/value.cpp @@ -1,134 +1,58 @@ -#include "ikarus/values/value.h" +#include "value.hpp" -#include +#include +#include +#include +#include +#include +#include -#include -#include +auto IkarusValue::from_json(nlohmann::json const & json) + -> cppbase::Result { + if (!json.is_object()) { + return cppbase::err(IkarusJsonError{IkarusJsonInvalidTypeError{}}); + } -// required for header-only inclusion -#include + IkarusValue value{}; -#include -#include -#include -#include -#include -#include + VTRY(value.schema, IkarusValueSchema::from_json(json["schema"])); + VTRY(value.data, IkarusValueData::from_json(json["data"])); -IkarusValue::IkarusValue(Data data, IkarusValueCardinality cardinality): - data{data}, - cardinality{cardinality} {} + if (!value.schema.validate(value.data)) { + return cppbase::err(IkarusValueParseErrorDataSchemaMismatch{}); + } -cppbase::Result IkarusValue::from_json(boost::json::value json) { - 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]() -> cppbase::Result { - 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>(*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()(); - } - case IkarusValueType_Number: { - return create_value.operator()(); - } - case IkarusValueType_Text: { - return create_value.operator()(); - } - default: { - return cppbase::err(FromJsonError{}); - } - } - } + return cppbase::ok(std::move(value)); } -boost::json::value IkarusValue::to_json() const { - auto type = std::visit( - cppbase::overloaded{ - []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusValueType_Toggle; }, - []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusValueType_Number; }, - []([[maybe_unused]] IkarusTextValue const * value) { return IkarusValueType_Text; } - }, - data - ); - - auto data_json = std::visit( - [](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} - }; +auto IkarusValue::to_json(nlohmann::json & json, IkarusValue const & value) + -> void { + IkarusValueSchema::to_json(json["schema"], value.schema); + IkarusValueData::to_json(json["data"], value.data); } -void ikarus_value_visit( - IkarusValue * value, - void (*toggle_visitor)(IkarusToggleValue *, void *), - void (*number_visitor)(IkarusNumberValue *, void *), - void (*text_visitor)(IkarusTextValue *, void *), - void * data -) { - std::visit( - cppbase::overloaded{ - [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); } - }, - value->data - ); +auto IkarusValues::from_json(nlohmann::json const & json) + -> cppbase::Result { + IkarusValues values{}; + + for (auto const & json_entry : json) { + VTRY(auto name_json, get_key(json_entry, "name")); + + if (!name_json->is_string()) { + return cppbase::err(IkarusJsonInvalidTypeError{}); + } + + VTRY(auto value_json, get_key(json_entry, "value")); + + VTRY(auto value, IkarusValue::from_json(*value_json)); + + values.values.emplace_back( + std::make_pair( + std::move(name_json->get()), + std::move(value) + ) + ); + } + + return cppbase::ok(std::move(values)); } diff --git a/src/ikarus/values/value.hpp b/src/ikarus/values/value.hpp index 07e92fc..47b3600 100644 --- a/src/ikarus/values/value.hpp +++ b/src/ikarus/values/value.hpp @@ -1,98 +1,28 @@ #pragma once -#include +#include #include -#include -#include -#include - -constexpr auto IKARUS_VALUE_JSON_TYPE_KEY = "type"; -constexpr auto IKARUS_VALUE_JSON_CARDINALITY_KEY = "card"; -constexpr auto IKARUS_VALUE_JSON_DATA_KEY = "data"; +#include +#include +#include struct IkarusValue { -public: - using Data = std::variant; - constexpr static auto SMALL_VEC_VALUE_SIZE = 8; + IkarusValueSchema schema; + IkarusValueData data; -public: - explicit IkarusValue(Data data, IkarusValueCardinality cardinality); - - IkarusValue(IkarusValue const &) = default; - IkarusValue(IkarusValue &&) noexcept = default; - - IkarusValue & operator=(IkarusValue const &) = default; - IkarusValue & operator=(IkarusValue &&) noexcept = default; - - virtual ~IkarusValue() = default; - -public: - struct FromJsonError {}; - - [[nodiscard]] static cppbase::Result from_json(boost::json::value json); - [[nodiscard]] boost::json::value to_json() const; - -public: - Data data; - IkarusValueCardinality cardinality; + static auto from_json(nlohmann::json const & json) + -> cppbase::Result; + static auto to_json(nlohmann::json & json, IkarusValue const & value) + -> void; }; -template<> -struct fmt::formatter { - template - constexpr static auto parse(FormatParseContext & ctx) { - return ctx.end(); - } +struct IkarusValues { + static auto from_json(nlohmann::json const & json) + -> cppbase::Result; + static auto to_json(nlohmann::json & json, IkarusValues const & values) + -> void; - constexpr static auto format([[maybe_unused]] IkarusValue::FromJsonError const & error, fmt::format_context & ctx) { - return fmt::format_to(ctx.out(), "unable to parse ikarus value JSON"); - } + std::vector> values; }; - -template -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(query, std::forward(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( \ - "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); diff --git a/src/ikarus/values/value_base.hpp b/src/ikarus/values/value_base.hpp deleted file mode 100644 index 5fa18ae..0000000 --- a/src/ikarus/values/value_base.hpp +++ /dev/null @@ -1,126 +0,0 @@ -#pragma once - -#include - -#include - -#include - -template -typename V::DataType const * ikarus_value_data_base_get(V * value, size_t idx) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - return &(*data)[idx]; - } - - return nullptr; -} - -template -size_t ikarus_value_data_base_get_size(V const * value) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - return data->size(); - } - - return 0; -} - -template -void ikarus_value_data_base_set(V * value, size_t idx, typename V::DataType new_data) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - (*data)[idx] = new_data; - } -} - -template -void ikarus_value_data_base_remove(V * value, size_t idx) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - data->erase(data->begin() + idx); - } -} - -template -void ikarus_value_data_base_insert(V * value, size_t idx, typename V::DataType new_data) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - data->insert(data->begin() + idx, new_data); - } -} - -template -void ikarus_value_data_base_clear(V * value) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - data->clear(); - } -} - -template -bool ikarus_value_data_base_is_undefined(V const * value) { - return boost::variant2::holds_alternative(value->data); -} - -template -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{}; - } -} - -template 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(transformer)), ", ") - ); - - return buffer.data(); - } - }, - value->data - ); -} - -template -bool ikarus_value_data_base_is_equal(V const * lhs, V const * rhs) { - return lhs->data == rhs->data; -} - -template -V * ikarus_value_data_base_copy(V const * value) { - return new V{*value}; -} - -template -struct IkarusValueData * ikarus_value_data_base_to_value(V * value) { - return static_cast(value); -} - -template -struct IkarusValueData const * ikarus_value_data_base_to_value_data_const(V const * value) { - return static_cast(value); -} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/tools/cmake/toolchains/mac.cmake b/tools/cmake/toolchains/mac.cmake new file mode 100644 index 0000000..b7120cd --- /dev/null +++ b/tools/cmake/toolchains/mac.cmake @@ -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") diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 611db2e..5b8f409 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -1,3 +1,8 @@ # order is important here since sqlitecpp otherwise duplicates cppbase add_subdirectory(cppbase) add_subdirectory(sqlitecpp) +add_subdirectory(json) + +if (LIBIKARUS_ENABLE_TESTS) + add_subdirectory(catch2) +endif () diff --git a/vendor/catch2 b/vendor/catch2 new file mode 160000 index 0000000..f5cee49 --- /dev/null +++ b/vendor/catch2 @@ -0,0 +1 @@ +Subproject commit f5cee49c71e7c0e805695b03e8af533783789762 diff --git a/vendor/json b/vendor/json new file mode 160000 index 0000000..6057b31 --- /dev/null +++ b/vendor/json @@ -0,0 +1 @@ +Subproject commit 6057b31df7fe0b958d1795defc108d1f26e26441 diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 32fa3c1..8edc04b 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 32fa3c1a9beae1a2c49fdd08cc659ea6153da666 +Subproject commit 8edc04b73bdee4b487ca59abd68615fa66c7948b From ba62206c0c84d5fbd4b2159331b07a5e66b15197 Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 1 Jan 2025 13:55:13 +0100 Subject: [PATCH 070/166] update vendor libraries Signed-off-by: Folling --- .gitmodules | 4 ++-- vendor/cppbase | 2 +- vendor/doxygen-awesome-css | 2 +- vendor/sqlitecpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index 94e3d41..029631e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,13 +3,13 @@ url = git@github.com:catchorg/Catch2.git [submodule "vendor/sqlitecpp"] path = vendor/sqlitecpp - url = ssh://git@git.rewritesarebliss.com:16658/Folling/sqlitecpp.git + url = ssh://git@git.foll.ing:59575/folling/sqlitecpp.git [submodule "vendor/doxygen-awesome-css"] path = vendor/doxygen-awesome-css url = git@github.com:jothepro/doxygen-awesome-css.git [submodule "vendor/cppbase"] path = vendor/cppbase - url = ssh://git@git.rewritesarebliss.com:16658/Folling/cppbase.git + url = ssh://git@git.foll.ing:59575/folling/cppbase.git [submodule "vendor/json"] path = vendor/json url = git@github.com:nlohmann/json.git diff --git a/vendor/cppbase b/vendor/cppbase index 6bf80cf..e7741e0 160000 --- a/vendor/cppbase +++ b/vendor/cppbase @@ -1 +1 @@ -Subproject commit 6bf80cf9d5ff54ab300f7e88a8cb9996f441dae6 +Subproject commit e7741e0e60f380f8b473c0881f3d8b4f23d0df91 diff --git a/vendor/doxygen-awesome-css b/vendor/doxygen-awesome-css index 00a52f6..af1d903 160000 --- a/vendor/doxygen-awesome-css +++ b/vendor/doxygen-awesome-css @@ -1 +1 @@ -Subproject commit 00a52f6c74065ffbd836cbd791ddfe8edf2836b8 +Subproject commit af1d9030b3ffa7b483fa9997a7272fb12af6af4c diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 8edc04b..5bfd32f 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 8edc04b73bdee4b487ca59abd68615fa66c7948b +Subproject commit 5bfd32f66164ed3d2c28003139fc5f76ef5e206d From e6233cf3f2afdf2dd2342613e65d6f2eff4336de Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 2 Jan 2025 09:39:08 +0100 Subject: [PATCH 071/166] finalize entity functions --- .clang-format | 2 +- include/ikarus/objects/entity.h | 41 +- src/ikarus/errors.hpp | 11 +- src/ikarus/objects/blueprint.cpp | 4 + src/ikarus/objects/blueprint.hpp | 16 +- src/ikarus/objects/entity.cpp | 385 ++++++++++++++++-- src/ikarus/objects/entity.hpp | 10 +- src/ikarus/objects/property.cpp | 9 +- src/ikarus/objects/property.hpp | 16 +- .../migrations/m0_initial_layout.sql | 5 +- src/ikarus/persistence/project.cpp | 18 +- src/ikarus/values/data.cpp | 21 +- src/ikarus/values/data.hpp | 3 +- src/ikarus/values/errors.hpp | 81 ++++ src/ikarus/values/schema.cpp | 30 +- src/ikarus/values/schema.hpp | 3 +- src/ikarus/values/value.cpp | 37 +- src/ikarus/values/value.hpp | 12 +- 18 files changed, 523 insertions(+), 181 deletions(-) diff --git a/.clang-format b/.clang-format index 6d3560a..5456c6f 100644 --- a/.clang-format +++ b/.clang-format @@ -64,7 +64,7 @@ BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon -BreakStringLiterals: true +BreakStringLiterals: false ColumnLimit: 80 CommentPragmas: '^\\.+' diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 0481da7..172bad1 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -181,8 +181,6 @@ IKA_API void ikarus_entity_link_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. @@ -203,17 +201,27 @@ IKA_API void ikarus_entity_unlink_blueprint( IkarusErrorData * error_out ); +/// \brief Struct for an entity value. +struct IkarusEntityValue { + /// \brief The name of the value. + char const * name; + /// \brief The value in json format. \see value.h + char const * value; +}; + /// \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 size_out An out parameter for the number of values in the returned array +/// or undefined if an error occurs. /// \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); +/// \return The values or null if an error occurs. +IKA_API IkarusEntityValue * ikarus_entity_get_values( + IkarusEntity * entity, + size_t * size_out, + IkarusErrorData * error_out +); /// \brief Gets a value of an entity. /// \param entity The entity to get the value of. @@ -279,16 +287,25 @@ IKA_API void ikarus_entity_delete_value( IkarusErrorData * error_out ); +/// \brief Struct for an entity property value. +struct IkarusEntityPropertyValue { + /// \brief The property. + struct IkarusProperty * property; + /// \brief The value in json format. \see value.h + char const * value; +}; + /// \brief Gets the property values of an entity. /// \param entity The entity to get the property values of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param size_out An out parameter for the number of property values in the +/// returned array or undefined if an error occurs. /// \param error_out \see errors.h -/// \return The property values, in msgpack format of or null if an error occurs. -/// The format is a map of property pointers (as integers) to values. \see -/// value.h -IKA_API char const * ikarus_entity_get_property_values( +/// \return The property values, or null if an error occurs. +IKA_API IkarusEntityPropertyValue * ikarus_entity_get_property_values( IkarusEntity * entity, + size_t * size_out, IkarusErrorData * error_out ); diff --git a/src/ikarus/errors.hpp b/src/ikarus/errors.hpp index 55b3e8f..a9daa9d 100644 --- a/src/ikarus/errors.hpp +++ b/src/ikarus/errors.hpp @@ -11,6 +11,8 @@ void safe_strcpy( size_t const dest_size ); +#define IKARUS_VOID_RETURN + #define IKARUS_SET_ERROR(msg, err_info) \ if (error_out != nullptr) { \ safe_strcpy( \ @@ -140,9 +142,9 @@ void safe_strcpy( IkarusErrorInfo_Client_InvalidInput \ ); -#define IKARUS_FAIL_IF_NOT_EXIST(object, ret) \ +#define IKARUS_FAIL_IF_NOT_EXIST_IMPL(exists_name, object, ret) \ IKARUS_VTRYRV_OR_FAIL( \ - auto exists, \ + auto exists_name, \ ret, \ fmt::format( \ "failed to check if {} exists", \ @@ -159,7 +161,7 @@ void safe_strcpy( ); \ \ IKARUS_FAIL_IF( \ - !exists, \ + !exists_name, \ ret, \ fmt::format( \ "{} doesn't exist", \ @@ -167,3 +169,6 @@ void safe_strcpy( ), \ IkarusErrorInfo_Client_NonExistent \ ) + +#define IKARUS_FAIL_IF_NOT_EXIST(object, ret) \ + IKARUS_FAIL_IF_NOT_EXIST_IMPL(CPPBASE_UNIQUE_NAME(exists), object, ret) diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index c25961f..553bbe2 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -3,3 +3,7 @@ #include #include #include + +IkarusBlueprint::IkarusBlueprint(struct IkarusProject * project, int64_t id): + project{project}, + id{id} {} diff --git a/src/ikarus/objects/blueprint.hpp b/src/ikarus/objects/blueprint.hpp index b56143f..64cd9ea 100644 --- a/src/ikarus/objects/blueprint.hpp +++ b/src/ikarus/objects/blueprint.hpp @@ -3,21 +3,11 @@ #include struct IkarusBlueprint { - consteval static inline auto OBJECT_NAME() -> std::string_view { - return "blueprint"; - } + constinit static inline auto object_name = "blueprint"; + constinit static inline auto table_name = "blueprints"; - consteval static inline auto TABLE_NAME() -> std::string_view { - return "blueprints"; - } - - IkarusBlueprint( - struct IkarusProject * project, - int64_t id, - std::string_view name - ); + IkarusBlueprint(struct IkarusProject * project, int64_t id); struct IkarusProject * project; int64_t id; - std::string name; }; diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 47f1f90..5abd085 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -1,18 +1,15 @@ #include "entity.hpp" +#include + #include #include #include #include -IkarusEntity::IkarusEntity( - struct IkarusProject * project, - int64_t id, - std::string_view name -): +IkarusEntity::IkarusEntity(struct IkarusProject * project, int64_t id): project{project}, - id{id}, - name{name} {} + id{id} {} IkarusEntity * ikarus_entity_create( struct IkarusProject * project, @@ -31,7 +28,7 @@ IkarusEntity * ikarus_entity_create( ); auto const id = project->db->last_insert_rowid(); - return new IkarusEntity{project, id, name}; + return new IkarusEntity{project, id}; } void ikarus_entity_delete( @@ -39,12 +36,11 @@ void ikarus_entity_delete( IkarusEntityDeleteFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, ); - IKARUS_FAIL_IF_NOT_EXIST(entity, ); - IKARUS_FAIL_IF_NULL(entity->project, ); + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); IKARUS_TRYRV_OR_FAIL( - , + IKARUS_VOID_RETURN, "failed to delete entity: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db @@ -61,7 +57,6 @@ IkarusEntity * ikarus_entity_copy( ) { 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, @@ -72,34 +67,28 @@ IkarusEntity * ikarus_entity_copy( [entity](auto * db) -> cppbase::Result { TRY(entity->project->db->execute( - "INSERT INTO `entities`(`name`) VALUES(?)", - entity->name.data() + "INSERT INTO `entities`(`name`) " + "SELECT `name` FROM `entities` WHERE `id` = ?", + entity->id )); TRY(entity->project->db->execute( - "INSERT INTO `entity_values`(`entity`, `name`, `value`)" - " SELECT ?1, `name`, `value` FROM `entity_values` WHERE " + "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` " + "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` " + "INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) " + "SELECT ?1, `property`, `value` FROM `entity_property_values` " "WHERE `entity` = ?1", entity->id )) @@ -109,13 +98,13 @@ IkarusEntity * ikarus_entity_copy( ) ); - return new IkarusEntity{entity->project, id, entity->name}; + return new IkarusEntity{entity->project, id}; } IkarusProject * ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NULL(entity->project, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); return entity->project; } @@ -123,8 +112,20 @@ ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) { char const * ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); - return entity->name.data(); + IKARUS_VTRYRV_OR_FAIL( + auto name, + nullptr, + "failed to get name for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT `name` FROM `entities` WHERE `id` = ?", + entity->id + ) + ); + + return name; } void ikarus_entity_set_name( @@ -133,11 +134,12 @@ void ikarus_entity_set_name( IkarusEntitySetNameFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, ); - IKARUS_FAIL_IF_NAME_INVALID(name, ); + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN); IKARUS_TRYRV_OR_FAIL( - , + IKARUS_VOID_RETURN, "failed to set name for entity: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->execute( @@ -146,8 +148,152 @@ void ikarus_entity_set_name( entity->id ) ); +} - entity->name = name; +struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( + IkarusEntity * entity, + size_t * size_out, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); + IKARUS_FAIL_IF_NULL(size_out, nullptr); + + auto count = ikarus_entity_get_linked_blueprints_count(entity, error_out); + IKARUS_FAIL_IF_ERROR(nullptr); + + std::int64_t ids[count]; + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to get linked blueprints for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_many_buffered( + "SELECT `blueprint` FROM `entity_blueprint_links` WHERE `entity` = ?", + ids, + count, + entity->id + ) + ); + + auto blueprints = new IkarusBlueprint *[count]; + + std::transform(ids, ids + count, blueprints, [entity](auto id) { + return new IkarusBlueprint{entity->project, id}; + }); + + if (size_out) { + *size_out = count; + } + + return blueprints; +} + +size_t ikarus_entity_get_linked_blueprints_count( + IkarusEntity * entity, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, 0); + IKARUS_FAIL_IF_NOT_EXIST(entity, 0); + + IKARUS_VTRYRV_OR_FAIL( + auto count, + 0, + "failed to get linked blueprints count for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", + entity->id + ) + ); + + return count; +} + +void ikarus_entity_link_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusEntityLinkBlueprintFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(blueprint, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(blueprint, IKARUS_VOID_RETURN); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to link blueprint to entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) VALUES(?, ?)", + entity->id, + blueprint->id + ) + ); +} + +void ikarus_entity_unlink_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusEntityUnlinkBlueprintFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(blueprint, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(blueprint, IKARUS_VOID_RETURN); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to unlink blueprint from entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "DELETE FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?", + entity->id, + blueprint->id + ) + ); +} + +IkarusEntityValue * ikarus_entity_get_values( + IkarusEntity * entity, + size_t * size_out, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto values_plain, + nullptr, + "failed to get values for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_many( + "SELECT `name`, `value` FROM `entity_values` WHERE `entity` = ?", + entity->id + ) + ); + + IkarusEntityValue * values = new IkarusEntityValue[values_plain.size()]; + + std::transform( + std::cbegin(values_plain), + std::cend(values_plain), + values, + [](auto const & tuple) { + return IkarusEntityValue{ + tuple.template get<0>(), + tuple.template get<1>() + }; + } + ); + + if (size_out) { + *size_out = values_plain.size(); + } + + return values; } char const * ikarus_entity_get_value( @@ -156,7 +302,22 @@ char const * ikarus_entity_get_value( IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); IKARUS_FAIL_IF_NULL(name, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto value, + nullptr, + "failed to get value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT `value` FROM `entity_values` WHERE `entity` = ? AND `name` = ?", + entity->id, + name + ) + ); + + return value; } void ikarus_entity_set_value( @@ -165,7 +326,155 @@ void ikarus_entity_set_value( char const * value, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, ); - IKARUS_FAIL_IF_NULL(name, ); - IKARUS_FAIL_IF_NULL(value, ); + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + + // parsing from & to here to ensure values are valid JSON & formatted + // uniformly + IKARUS_VTRYRV_OR_FAIL( + auto value_parsed, + IKARUS_VOID_RETURN, + "cannot parse value as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValue::from_json(value) + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "INSERT INTO `entity_values`(`entity`, `name`, `value`) VALUES(?, ?, ?) " + "ON CONFLICT(`entity`, `name`) DO UPDATE SET `value` = excluded.`value`", + entity->id, + name, + IkarusValue::to_json(value_parsed).dump() + ) + ); +} + +void ikarus_entity_delete_value( + IkarusEntity * entity, + char const * name, + IkarusEntityDeleteValueFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to delete value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "DELETE FROM `entity_values` WHERE `entity` = ? AND `name` = ?", + entity->id, + name + ) + ); +} + +IkarusEntityPropertyValue * ikarus_entity_get_property_values( + IkarusEntity * entity, + size_t * size_out, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto values_plain, + nullptr, + "failed to get property values for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_many( + "SELECT `property`, `value` FROM `entity_property_values` WHERE `entity` = ?", + entity->id + ) + ); + + IkarusEntityPropertyValue * values = + new IkarusEntityPropertyValue[values_plain.size()]; + std::transform( + std::cbegin(values_plain), + std::cend(values_plain), + values, + [entity](auto const & tuple) { + return IkarusEntityPropertyValue{ + new IkarusProperty{entity->project, tuple.template get<0>()}, + tuple.template get<1>() + }; + } + ); + + if (size_out) { + *size_out = values_plain.size(); + } + + return values; +} + +char const * ikarus_entity_get_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); + IKARUS_FAIL_IF_NULL(property, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(property, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto value, + nullptr, + "failed to get property value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT `value` FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?", + entity->id, + property->id + ) + ); + + return value; +} + +void ikarus_entity_set_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + char const * value, + IkarusEntitySetPropertyValueFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(property, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(property, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + + // parsing from & to here to ensure values are valid JSON & formatted + // uniformly + IKARUS_VTRYRV_OR_FAIL( + auto value_parsed, + IKARUS_VOID_RETURN, + "cannot parse value as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValue::from_json(value) + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set property value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "INSERT INTO `entity_property_values`(`entity`, `property`, `value`) VALUES(?, ?, ?) " + "ON CONFLICT(`entity`, `property`) DO UPDATE SET `value` = excluded.`value`", + entity->id, + property->id, + IkarusValue::to_json(value_parsed).dump() + ) + ); } diff --git a/src/ikarus/objects/entity.hpp b/src/ikarus/objects/entity.hpp index 90a445e..449d69e 100644 --- a/src/ikarus/objects/entity.hpp +++ b/src/ikarus/objects/entity.hpp @@ -9,16 +9,8 @@ struct IkarusEntity { constinit static inline auto object_name = "entity"; constinit static inline auto table_name = "entities"; - IkarusEntity( - struct IkarusProject * project, - int64_t id, - std::string_view name - ); + IkarusEntity(struct IkarusProject * project, int64_t id); struct IkarusProject * project; int64_t id; - std::string name; - - std::vector> values_ordered; - std::unordered_map values; }; diff --git a/src/ikarus/objects/property.cpp b/src/ikarus/objects/property.cpp index d096b18..43fac96 100644 --- a/src/ikarus/objects/property.cpp +++ b/src/ikarus/objects/property.cpp @@ -5,11 +5,6 @@ #include #include -IkarusProperty::IkarusProperty( - struct IkarusProject * project, - int64_t id, - std::string_view name -): +IkarusProperty::IkarusProperty(struct IkarusProject * project, int64_t id): project{project}, - id{id}, - name{name} {} + id{id} {} diff --git a/src/ikarus/objects/property.hpp b/src/ikarus/objects/property.hpp index 29143fb..32bbbc9 100644 --- a/src/ikarus/objects/property.hpp +++ b/src/ikarus/objects/property.hpp @@ -3,21 +3,11 @@ #include struct IkarusProperty { - consteval static inline auto OBJECT_NAME() -> std::string_view { - return "property"; - } + constinit static inline auto object_name = "property"; + constinit static inline auto table_name = "properties"; - consteval static inline auto TABLE_NAME() -> std::string_view { - return "properties"; - } - - IkarusProperty( - struct IkarusProject * project, - int64_t id, - std::string_view name - ); + IkarusProperty(struct IkarusProject * project, int64_t id); struct IkarusProject * project; int64_t id; - std::string name; }; diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index 8b263d7..e83b41a 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -6,7 +6,6 @@ CREATE TABLE `entities` CREATE TABLE `entity_values` ( - `id` INTEGER PRIMARY KEY, `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `name` TEXT NOT NULL, `value` TEXT NOT NULL, @@ -36,12 +35,12 @@ CREATE TABLE `entity_blueprint_links` `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE, - PRIMARY KEY (`entity`, `blueprint`) + PRIMARY KEY (`entity`, `blueprint`), + FOREIGN KEY (`entity`, `blueprint`) REFERENCES `entity_blueprint_links` (`entity`, `blueprint`) ON DELETE CASCADE ) STRICT; CREATE TABLE `entity_property_values` ( - `id` INTEGER PRIMARY KEY, `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `property` INTEGER NOT NULL REFERENCES `properties` (`id`) ON DELETE CASCADE, `value` TEXT NOT NULL, diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 300bf86..75c8825 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -232,18 +232,18 @@ void ikarus_project_set_name( char const * new_name, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, ); - IKARUS_FAIL_IF_NULL(new_name, ); + IKARUS_FAIL_IF_NULL(project, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(new_name, IKARUS_VOID_RETURN); IKARUS_FAIL_IF( cppbase::is_empty_or_blank(new_name), - , + IKARUS_VOID_RETURN, "name must not be empty", IkarusErrorInfo_Client_InvalidInput ); IKARUS_TRYRV_OR_FAIL( - , + IKARUS_VOID_RETURN, "failed to update project name: {}", IkarusErrorInfo_Database_QueryFailed, project->db->execute( @@ -269,15 +269,15 @@ void ikarus_project_get_entities( uint64_t ids_out_size, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, ); - IKARUS_FAIL_IF_NULL(ids_out, ); + IKARUS_FAIL_IF_NULL(project, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(ids_out, IKARUS_VOID_RETURN); if (ids_out == 0) { return; } IKARUS_TRYRV_OR_FAIL( - , + IKARUS_VOID_RETURN, "unable to fetch project entities from database: {}", IkarusErrorInfo_Database_QueryFailed, project->db->query_many_buffered( @@ -311,8 +311,8 @@ void ikarus_project_get_blueprints( uint64_t ids_out_size, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, ); - IKARUS_FAIL_IF_NULL(ids_out, ); + IKARUS_FAIL_IF_NULL(project, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(ids_out, IKARUS_VOID_RETURN); if (ids_out == 0) { return; diff --git a/src/ikarus/values/data.cpp b/src/ikarus/values/data.cpp index ac893b2..f7f6845 100644 --- a/src/ikarus/values/data.cpp +++ b/src/ikarus/values/data.cpp @@ -152,10 +152,9 @@ auto IkarusValueData::from_json(nlohmann::json const & json) return cppbase::ok(value); } -auto IkarusValueData::to_json( - nlohmann::json & json, - IkarusValueData const & value -) -> void { +auto IkarusValueData::to_json(IkarusValueData const & value) -> nlohmann::json { + nlohmann::json json = nlohmann::json::object(); + std::visit( cppbase::overloaded{ [&](IkarusValueDataPrimitive const & primitive) { @@ -184,9 +183,7 @@ auto IkarusValueData::to_json( json["type"] = IkarusValueDataType_List; json["data"] = list.values | std::views::transform([](auto const & data) { - nlohmann::json j; - IkarusValueData::to_json(j, *data); - return j; + return IkarusValueData::to_json(*data); }) | std::ranges::to>(); }, @@ -195,8 +192,8 @@ auto IkarusValueData::to_json( 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); + j["key"] = IkarusValueData::to_json(*pair.first); + j["value"] = IkarusValueData::to_json(*pair.second); return j; }) | std::ranges::to>(); @@ -205,13 +202,13 @@ auto IkarusValueData::to_json( json["type"] = IkarusValueDataType_Tuple; json["data"] = tuple.values | std::views::transform([](auto const & data) { - nlohmann::json j; - IkarusValueData::to_json(j, *data); - return j; + return IkarusValueData::to_json(*data); }) | std::ranges::to>(); } }, value.variant ); + + return json; } diff --git a/src/ikarus/values/data.hpp b/src/ikarus/values/data.hpp index d4e92ad..1c9b3d8 100644 --- a/src/ikarus/values/data.hpp +++ b/src/ikarus/values/data.hpp @@ -55,8 +55,7 @@ struct IkarusValueData { static auto from_json(nlohmann::json const & json) -> cppbase::Result; - static auto to_json(nlohmann::json & json, IkarusValueData const & value) - -> void; + static auto to_json(IkarusValueData const & value) -> nlohmann::json; IkarusValueDataVariant variant; }; diff --git a/src/ikarus/values/errors.hpp b/src/ikarus/values/errors.hpp index ed752af..f472d83 100644 --- a/src/ikarus/values/errors.hpp +++ b/src/ikarus/values/errors.hpp @@ -2,6 +2,8 @@ #include +#include + struct IkarusJsonMissingKeyError {}; struct IkarusJsonInvalidTypeError {}; @@ -34,3 +36,82 @@ using IkarusValueParseError = std::variant< using IkarusValuesParseError = std::variant; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusJsonMissingKeyError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "missing JSON key"); + } +}; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusJsonInvalidTypeError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "invalid JSON type"); + } +}; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusJsonEnumOutOfBoundsError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "JSON enum is out of bounds"); + } +}; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusJsonUnknownError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "unknown JSON error"); + } +}; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusValueSchemaParseError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to( + ctx.out(), + "failed to parse value schema: {}", + error.error + ); + } +}; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusValueDataParseError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to( + ctx.out(), + "failed to parse value data: {}", + error.error + ); + } +}; + +template<> +struct fmt::formatter : + formatter { + constexpr static auto format( + [[maybe_unused]] IkarusValueParseErrorDataSchemaMismatch const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "value data and schema mismatched"); + } +}; diff --git a/src/ikarus/values/schema.cpp b/src/ikarus/values/schema.cpp index e6cbd24..ea2e6dc 100644 --- a/src/ikarus/values/schema.cpp +++ b/src/ikarus/values/schema.cpp @@ -85,10 +85,10 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) return cppbase::ok(std::move(schema)); } -auto IkarusValueSchema::to_json( - nlohmann::json & json, - IkarusValueSchema const & schema -) -> void { +auto IkarusValueSchema::to_json(IkarusValueSchema const & schema) + -> nlohmann::json { + nlohmann::json json = nlohmann::json::object(); + std::visit( cppbase::overloaded{ [&json](IkarusValueSchemaPrimitive const & schema) { @@ -97,18 +97,14 @@ auto IkarusValueSchema::to_json( }, [&json](IkarusValueSchemaList const & schema) { json["type"] = IkarusValueSchemaType_List; - IkarusValueSchema::to_json(json["schema"], *schema.sub_schema); + json["schema"] = IkarusValueSchema::to_json(*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["key_schema"] = + IkarusValueSchema::to_json(*schema.key_schema); + json["value_schema"] = + IkarusValueSchema::to_json(*schema.value_schema); }, [&json](IkarusValueSchemaTuple const & schema) { json["type"] = IkarusValueSchemaType_Tuple; @@ -117,9 +113,9 @@ auto IkarusValueSchema::to_json( 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); + sub_schemas.push_back( + IkarusValueSchema::to_json(*sub_schema) + ); } json["schemas"] = sub_schemas; @@ -127,6 +123,8 @@ auto IkarusValueSchema::to_json( }, schema.variant ); + + return json; } auto IkarusValueSchema::validate(IkarusValueData const & data) const -> bool { diff --git a/src/ikarus/values/schema.hpp b/src/ikarus/values/schema.hpp index 4f4256c..7fd07ab 100644 --- a/src/ikarus/values/schema.hpp +++ b/src/ikarus/values/schema.hpp @@ -42,8 +42,7 @@ struct IkarusValueSchema { static auto from_json(nlohmann::json const & json) -> cppbase::Result; - static auto to_json(nlohmann::json & json, IkarusValueSchema const & value) - -> void; + static auto to_json(IkarusValueSchema const & value) -> nlohmann::json; auto validate(IkarusValueData const & data) const -> bool; diff --git a/src/ikarus/values/value.cpp b/src/ikarus/values/value.cpp index 07c0c24..9a23a98 100644 --- a/src/ikarus/values/value.cpp +++ b/src/ikarus/values/value.cpp @@ -25,34 +25,11 @@ auto IkarusValue::from_json(nlohmann::json const & json) return cppbase::ok(std::move(value)); } -auto IkarusValue::to_json(nlohmann::json & json, IkarusValue const & value) - -> void { - IkarusValueSchema::to_json(json["schema"], value.schema); - IkarusValueData::to_json(json["data"], value.data); -} - -auto IkarusValues::from_json(nlohmann::json const & json) - -> cppbase::Result { - IkarusValues values{}; - - for (auto const & json_entry : json) { - VTRY(auto name_json, get_key(json_entry, "name")); - - if (!name_json->is_string()) { - return cppbase::err(IkarusJsonInvalidTypeError{}); - } - - VTRY(auto value_json, get_key(json_entry, "value")); - - VTRY(auto value, IkarusValue::from_json(*value_json)); - - values.values.emplace_back( - std::make_pair( - std::move(name_json->get()), - std::move(value) - ) - ); - } - - return cppbase::ok(std::move(values)); +auto IkarusValue::to_json(IkarusValue const & value) -> nlohmann::json { + nlohmann::json json = nlohmann::json::object(); + + json["schema"] = IkarusValueSchema::to_json(value.schema); + json["data"] = IkarusValueData::to_json(value.data); + + return json; } diff --git a/src/ikarus/values/value.hpp b/src/ikarus/values/value.hpp index 47b3600..74db914 100644 --- a/src/ikarus/values/value.hpp +++ b/src/ikarus/values/value.hpp @@ -14,15 +14,5 @@ struct IkarusValue { static auto from_json(nlohmann::json const & json) -> cppbase::Result; - static auto to_json(nlohmann::json & json, IkarusValue const & value) - -> void; -}; - -struct IkarusValues { - static auto from_json(nlohmann::json const & json) - -> cppbase::Result; - static auto to_json(nlohmann::json & json, IkarusValues const & values) - -> void; - - std::vector> values; + static auto to_json(IkarusValue const & value) -> nlohmann::json; }; From ef27673846a4c12e31fbffa996cd9c142f4aca65 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 2 Jan 2025 09:39:58 +0100 Subject: [PATCH 072/166] update dependencies --- .clang-format | 3 +- include/ikarus/errors.h | 13 +- include/ikarus/objects/blueprint.h | 38 +- include/ikarus/objects/entity.h | 80 ++- include/ikarus/objects/property.h | 29 +- src/ikarus/errors.cpp | 4 +- src/ikarus/errors.hpp | 200 +++---- src/ikarus/objects/blueprint.cpp | 64 +++ src/ikarus/objects/entity.cpp | 508 ++++++++++++------ .../migrations/m0_initial_layout.sql | 3 +- src/ikarus/persistence/project.cpp | 43 ++ src/ikarus/values/data.cpp | 53 +- src/ikarus/values/data.hpp | 8 +- src/ikarus/values/errors.hpp | 13 + src/ikarus/values/schema.cpp | 80 ++- src/ikarus/values/schema.hpp | 4 + src/ikarus/values/shared.hpp | 4 +- src/ikarus/values/value.cpp | 17 +- src/ikarus/values/value.hpp | 2 + vendor/cppbase | 2 +- vendor/sqlitecpp | 2 +- 21 files changed, 800 insertions(+), 370 deletions(-) diff --git a/.clang-format b/.clang-format index 5456c6f..f88191b 100644 --- a/.clang-format +++ b/.clang-format @@ -66,7 +66,7 @@ BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon BreakStringLiterals: false -ColumnLimit: 80 +ColumnLimit: 120 CommentPragmas: '^\\.+' CompactNamespaces: false ConstructorInitializerIndentWidth: 4 @@ -148,6 +148,7 @@ PPIndentWidth: -1 PackConstructorInitializers: Never PointerAlignment: Middle + QualifierAlignment: Right # QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ] ReferenceAlignment: Middle diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index a41c291..eb123b0 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -31,24 +31,27 @@ enum IkarusErrorInfo { /// \brief The client provided a non-existent resource. /// Example: Passing an entity to a function after it has been deleted. IkarusErrorInfo_Client_NonExistent = 0x01000003, + /// \brief The client provided a resource which exists but is not linked to the current context. + /// Example: Passing a property that isn't linked to the current entity. + IkarusErrorInfo_Client_NotLinked = 0x01000004, /// \brief The client provided an index that was out of bounds for some array. /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3. - IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000004, + IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000005, /// \brief The client provided a numeric value that was out of bounds /// Example: Passing the value 2^32 to an i32 (might be passed as a string). - IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000005, + IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000006, /// \brief The client provided invalid input that doesn't fit in any of the other categories. /// Example: Passing an empty/blank string for a string that must be /// non-empty/-blank. - IkarusErrorInfo_Client_InvalidInput = 0x01000006, + IkarusErrorInfo_Client_InvalidInput = 0x01000007, /// \brief The client provided valid data in an invalid format. /// Example: Passing a malformed JSON string. - IkarusErrorInfo_Client_InvalidFormat = 0x01000007, + IkarusErrorInfo_Client_InvalidFormat = 0x01000008, /// \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, + IkarusErrorInfo_Client_ConstraintViolated = 0x10000009, // 0x02 reserved for dependency errors diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index adc97e9..7df09a8 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -20,6 +20,13 @@ IKARUS_BEGIN_HEADER /// in all linked entities. struct IkarusBlueprint; +/// \brief Checks whether a blueprint exists. +/// \param blueprint The blueprint to check. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return True if the blueprint exists, false otherwise or if an error occurs. +IKA_API bool ikarus_blueprint_exists(IkarusBlueprint * blueprint, IkarusErrorData * error_out); + /// \brief Flags for creating a blueprint. enum IkarusBlueprintCreateFlags { /// \brief No flags. @@ -48,14 +55,10 @@ IKA_API IkarusBlueprint * ikarus_blueprint_create( 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. +/// \details Each value of the entity will be copied into the blueprint as a blueprint. /// \param entity The entity to create the blueprint from. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -117,10 +120,8 @@ IKA_API void ikarus_blueprint_delete( /// \param error_out \see errors.h /// \return The project the blueprint belongs to. /// \remark Ownership remains with libikarus. -IKA_API struct IkarusProject * ikarus_blueprint_get_project( - struct IkarusBlueprint * blueprint, - struct IkarusErrorData * error_out -); +IKA_API struct IkarusProject * +ikarus_blueprint_get_project(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); /// \brief Gets the name of a blueprint. /// \param blueprint The blueprint to get the name of. @@ -129,10 +130,7 @@ IKA_API struct IkarusProject * ikarus_blueprint_get_project( /// \param error_out \see errors.h /// \return The name of the blueprint. /// \remark Ownership remains with libikarus. -IKA_API char const * ikarus_blueprint_get_name( - struct IkarusBlueprint * blueprint, - struct IkarusErrorData * error_out -); +IKA_API char const * ikarus_blueprint_get_name(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); /// \brief Flags for setting the name of a blueprint. enum IkarusBlueprintSetNameFlags { @@ -163,7 +161,7 @@ IKA_API void ikarus_blueprint_set_name( /// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs. /// \param error_out \see errors.h /// \return The properties of the blueprint or null if an error occurs. -IKA_API struct IkarusProperty ** ikarus_blueprint_get_properties( +IKA_API struct IkarusBlueprint ** ikarus_blueprint_get_properties( struct IkarusBlueprint * blueprint, size_t * size_out, struct IkarusErrorData * error_out @@ -175,10 +173,8 @@ IKA_API struct IkarusProperty ** ikarus_blueprint_get_properties( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of properties of the blueprint or 0 if an error occurs. -IKA_API size_t ikarus_blueprint_get_properties_count( - struct IkarusBlueprint * blueprint, - struct IkarusErrorData * error_out -); +IKA_API size_t +ikarus_blueprint_get_properties_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); /// \brief Gets all entities linked to a blueprint. /// \param blueprint The blueprint to get the entities of. @@ -200,10 +196,8 @@ IKA_API struct IkarusEntity ** ikarus_blueprint_get_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of entities linked to the blueprint or 0 if an error occurs. -IKA_API size_t ikarus_blueprint_get_entities_count( - struct IkarusBlueprint * blueprint, - struct IkarusErrorData * error_out -); +IKA_API size_t +ikarus_blueprint_get_entities_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 172bad1..990be61 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -33,6 +33,14 @@ enum IkarusEntityCreateFlags { IkarusEntityCreateFlags_None = 0, }; +/// \brief Checks whether an entity exists. +/// \param entity The entity to check. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return True if the entity exists, false otherwise or if an error occurs. +IKA_API bool +ikarus_entity_exists(IkarusEntity * entity, IkarusErrorData * error_out); + /// \brief Creates a new entity. /// \param project The project to create the entity in. /// \pre \li Must not be null. @@ -127,6 +135,22 @@ IKA_API void ikarus_entity_set_name( IkarusErrorData * error_out ); +/// \brief Gets whether an entity is linked to a blueprint. +/// \param entity The entity to check the blueprint of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprint The blueprint to check the entity's link to. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \pre \li Must be in the same project as the entity. +/// \param error_out \see errors.h +/// \return True if the entity is linked to the blueprint, false otherwise or if an error occurs. +IKA_API bool ikarus_entity_is_linked_to_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusErrorData * error_out +); + /// \brief Gets the blueprints an entity is linked to. /// \param entity The entity to get the blueprints of. /// \pre \li Must not be null. @@ -191,6 +215,7 @@ enum IkarusEntityUnlinkBlueprintFlags { /// \param blueprint The blueprint to unlink from. /// \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 not linked to the blueprint, nothing happens. /// \param flags Flags for unlinking the entity from the blueprint. /// \param error_out \see errors.h @@ -201,6 +226,20 @@ IKA_API void ikarus_entity_unlink_blueprint( IkarusErrorData * error_out ); +/// \brief Gets whether an entity has a value. +/// \param entity The entity to check the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The value's name. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return True if the entity has a value with the name, false otherwise or if an error occurs. +IKA_API bool ikarus_entity_has_value( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out +); + /// \brief Struct for an entity value. struct IkarusEntityValue { /// \brief The name of the value. @@ -229,7 +268,7 @@ IKA_API IkarusEntityValue * ikarus_entity_get_values( /// \pre \li Must exist. /// \param name The value's name. /// \pre \li Must not be null. -/// \remark Ownership remains with the client. +/// \pre \li Must exist. /// \param error_out \see errors.h /// \return The value, in json format of or null if an error occurs. \see value.h IKA_API char const * ikarus_entity_get_value( @@ -287,6 +326,22 @@ IKA_API void ikarus_entity_delete_value( IkarusErrorData * error_out ); +/// \brief Gets whether an entity has a property value. +/// \param entity The entity to check the property value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to check the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \pre \li Must be in the same project as the entity. +/// \param error_out \see errors.h +/// \return True if the entity has a value for the property, false otherwise or if an error occurs. +IKA_API bool ikarus_entity_has_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + IkarusErrorData * error_out +); + /// \brief Struct for an entity property value. struct IkarusEntityPropertyValue { /// \brief The property. @@ -355,6 +410,29 @@ IKA_API void ikarus_entity_set_property_value( IkarusErrorData * error_out ); +/// \brief Flags for clearing the value of a property of an entity. +enum IkarusEntityClearPropertyValueFlags { + /// \brief No flags. + IkarusEntityClearPropertyValueFlags_None = 0, +}; + +/// \brief Clears the value of a property of an entity. +/// \param entity The entity to clear the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to clear the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \pre \li Must be linked to the entity. +/// \param flags Flags for clearing the property value. +/// \param error_out \see errors.h +IKA_API void ikarus_entity_clear_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + IkarusEntitySetPropertyValueFlags flags, + IkarusErrorData * error_out +); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index 5bff42c..2124423 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -36,6 +36,13 @@ IKARUS_BEGIN_HEADER /// property's default value if none is specified. struct IkarusProperty; +/// \brief Checks whether a property exists. +/// \param property The property to check. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return True if the property exists, false otherwise or if an error occurs. +IKA_API bool ikarus_property_exists(IkarusProperty * property, IkarusErrorData * error_out); + /// \brief Flags for creating a property. enum IkarusPropertyCreateFlags { /// \brief No flags. @@ -73,11 +80,8 @@ enum IkarusPropertyDeleteFlags { /// \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 -); +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. @@ -86,10 +90,7 @@ IKA_API void ikarus_property_delete( /// \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 -); +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. @@ -98,10 +99,7 @@ IKA_API struct IkarusProject * ikarus_property_get_project( /// \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 -); +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. @@ -110,10 +108,7 @@ IKA_API char const * ikarus_property_get_name( /// \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 -); +IKA_API struct IkarusValueSchema * ikarus_property_get_schema(IkarusProperty * property, IkarusErrorData * error_out); /// \brief Flags for setting the name of a property. enum IkarusPropertySetNameFlags { diff --git a/src/ikarus/errors.cpp b/src/ikarus/errors.cpp index b3e4ae2..07f7a15 100644 --- a/src/ikarus/errors.cpp +++ b/src/ikarus/errors.cpp @@ -64,9 +64,9 @@ char const * ikarus_get_error_info_name(IkarusErrorInfo info) { } bool ikarus_error_data_is_success(IkarusErrorData const * data) { - return data->info == IkarusErrorInfo_None; + return data && data->info == IkarusErrorInfo_None; } bool ikarus_error_data_is_error(IkarusErrorData const * data) { - return data->info != IkarusErrorInfo_None; + return data && data->info != IkarusErrorInfo_None; } diff --git a/src/ikarus/errors.hpp b/src/ikarus/errors.hpp index a9daa9d..d4294ec 100644 --- a/src/ikarus/errors.hpp +++ b/src/ikarus/errors.hpp @@ -3,24 +3,33 @@ #include #include +#include +#include +#include + #include -void safe_strcpy( - std::string_view const src, - char * dest, - size_t const dest_size -); +void safe_strcpy(std::string_view const src, char * dest, size_t const dest_size); + +template<> +struct fmt::formatter : formatter { + constexpr static auto format([[maybe_unused]] IkarusErrorData const & error, fmt::format_context & ctx) { + return fmt::format_to( + ctx.out(), + "ERROR {}({}): {}", + ikarus_error_info_get_name(error.info), + static_cast>(error.info), + error.message + ); + } +}; #define IKARUS_VOID_RETURN -#define IKARUS_SET_ERROR(msg, err_info) \ - if (error_out != nullptr) { \ - safe_strcpy( \ - msg, \ - static_cast(error_out->message), \ - IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT \ - ); \ - error_out->info = err_info; \ +#define IKARUS_SET_ERROR(msg, err_info) \ + if (error_out != nullptr) { \ + safe_strcpy(msg, static_cast(error_out->message), IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT); \ + error_out->info = err_info; \ } #define IKARUS_FAIL(ret, msg, err_info) \ @@ -33,142 +42,75 @@ void safe_strcpy( return ret; \ } -#define IKARUS_TRY_OR_FAIL_IMPL(var_name, msg, err_info, ...) \ - auto var_name = __VA_ARGS__; \ - 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_TRY_OR_FAIL_IMPL(var_name, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + 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_TRY_OR_FAIL(msg, err_info, ...) \ - IKARUS_TRY_OR_FAIL_IMPL( \ - CPPBASE_UNIQUE_NAME(result), \ - msg, \ - err_info, \ - __VA_ARGS__ \ - ); + IKARUS_TRY_OR_FAIL_IMPL(CPPBASE_UNIQUE_NAME(result), msg, err_info, __VA_ARGS__); -#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR( \ - fmt::format( \ - fmt::runtime(msg), \ - std::move(var_name).unwrap_error() \ - ), \ - err_info \ - ); \ - return ret; \ +#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(fmt::runtime(msg), std::move(var_name).unwrap_error()), err_info); \ + return ret; \ } #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, ...) \ - auto var_name = __VA_ARGS__; \ - 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_VTRY_OR_FAIL_IMPL(var_name, value, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(fmt::runtime(msg), 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, ...) \ - 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, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR( \ - fmt::format(fmt::runtime(msg), var_name.unwrap_error()), \ - err_info \ - ); \ - return ret; \ - } \ +#define IKARUS_VTRYRV_OR_FAIL_IMPL(var_name, value, ret, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(fmt::runtime(msg), var_name.unwrap_error()), err_info); \ + return ret; \ + } \ value = std::move(var_name).unwrap_value() #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_ERROR(ret, error) \ + if (ikarus_error_data_is_error(error)) { \ + if (error_out) { \ + *error_out = *error; \ + } \ + 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_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 \ - ); + IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput) -#define IKARUS_FAIL_IF_NOT_EXIST_IMPL(exists_name, object, ret) \ - IKARUS_VTRYRV_OR_FAIL( \ - auto exists_name, \ - ret, \ - fmt::format( \ - "failed to check if {} exists", \ - std::remove_cvref_t::object_name \ - ), \ - IkarusErrorInfo_Database_QueryFailed, \ - object->project->db->query_one( \ - fmt::format( \ - "SELECT EXISTS(SELECT 1 FROM `{}` WHERE `id` = ?)", \ - std::remove_cvref_t::table_name \ - ), \ - object->id \ - ) \ - ); \ - \ - IKARUS_FAIL_IF( \ - !exists_name, \ - ret, \ - fmt::format( \ - "{} doesn't exist", \ - std::remove_cvref_t::object_name \ - ), \ - IkarusErrorInfo_Client_NonExistent \ - ) +#define IKARUS_ASCERTAIN_IMPL(cond_name, ret, msg, err_info, func, ...) \ + auto cond_name = std::invoke(func, __VA_ARGS__, error_out); \ + IKARUS_FAIL_IF_ERROR(ret, error_out) \ + IKARUS_FAIL_IF(!cond_name, ret, msg, err_info) -#define IKARUS_FAIL_IF_NOT_EXIST(object, ret) \ - IKARUS_FAIL_IF_NOT_EXIST_IMPL(CPPBASE_UNIQUE_NAME(exists), object, ret) +#define IKARUS_ASCERTAIN(ret, msg, err_info, func, ...) \ + IKARUS_ASCERTAIN_IMPL(CPPBASE_UNIQUE_NAME(cond), ret, msg, err_info, func, __VA_ARGS__); + +#define IKARUS_CALL(ret, func, ...) \ + std::invoke(func, __VA_ARGS__, error_out); \ + IKARUS_FAIL_IF_ERROR(ret, error_out); + +#define IKARUS_VCALL(value, ret, func, ...) \ + value = std::invoke(func, __VA_ARGS__, error_out); \ + IKARUS_FAIL_IF_ERROR(ret, error_out); diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index 553bbe2..ba9e7b1 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -2,8 +2,72 @@ #include #include +#include #include IkarusBlueprint::IkarusBlueprint(struct IkarusProject * project, int64_t id): project{project}, id{id} {} + +IkarusBlueprint * ikarus_blueprint_create( + struct IkarusProject * project, + char const * name, + IkarusBlueprintCreateFlags flags, + struct IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to create entity: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->execute("INSERT INTO `blueprints`(`name`) VALUES(?)", name) + ); + + auto const id = project->db->last_insert_rowid(); + return new IkarusBlueprint{project, id}; +} + +IkarusBlueprint * ikarus_blueprint_create_from_entity( + struct IkarusEntity * entity, + char const * name, + IkarusBlueprintCreateFromEntityFlags flags, + struct IkarusErrorData * error_out +) { + IKARUS_TRYRV_OR_FAIL( + nullptr, + "{}", + IkarusErrorInfo_Database_QueryFailed, + ikarus_libikarus_func_call_to_result( + error_out, + ikarus_must_return_true(ikarus_entity_exists, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent), + entity + ) + ); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto id, + nullptr, + "failed to create blueprint from entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->transact([&](auto * db) -> cppbase::Result { + CPPBASE_TRY(db->execute("INSERT INTO `blueprints`(`name`) VALUES(?)", name)); + + auto const id = db->last_insert_rowid(); + + CPPBASE_TRY(entity->project->db->execute( + "INSERT INTO `properties`(`blueprint`, `name`, `schema`) " + "SELECT ?, `name`, json_extract(`value`, '$.schema') FROM `entity_values` " + "WHERE `entity` = ?", + id, + entity->id + )) + + return cppbase::ok(entity->project->db->last_insert_rowid()); + }) + ); + + return new IkarusBlueprint{entity->project, id}; +} diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 5abd085..aae7564 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -4,13 +4,28 @@ #include #include +#include #include +#include #include IkarusEntity::IkarusEntity(struct IkarusProject * project, int64_t id): project{project}, id{id} {} +bool ikarus_entity_exists(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(entity, false); + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check whether entity exists: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one("SELECT EXISTS(SELECT 1 FROM `entities` WHERE `id` = ?)", entity->id) + ); + + return exists; +} + IkarusEntity * ikarus_entity_create( struct IkarusProject * project, char const * name, @@ -31,98 +46,83 @@ IkarusEntity * ikarus_entity_create( return new IkarusEntity{project, id}; } -void ikarus_entity_delete( - IkarusEntity * entity, - IkarusEntityDeleteFlags flags, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); +void ikarus_entity_delete(IkarusEntity * entity, IkarusEntityDeleteFlags flags, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); IKARUS_TRYRV_OR_FAIL( IKARUS_VOID_RETURN, "failed to delete entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db - ->execute("DELETE FROM `entities` WHERE `id` = ?", entity->id) + entity->project->db->execute("DELETE FROM `entities` WHERE `id` = ?", entity->id) ); delete entity; } -IkarusEntity * ikarus_entity_copy( - IkarusEntity * entity, - IkarusEntityCopyFlags flags, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); +IkarusEntity * ikarus_entity_copy(IkarusEntity * entity, IkarusEntityCopyFlags flags, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto id, nullptr, "failed to copy entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->transact( - [entity](auto * db) - -> cppbase::Result { - TRY(entity->project->db->execute( - "INSERT INTO `entities`(`name`) " - "SELECT `name` FROM `entities` WHERE `id` = ?", - entity->id - )); + entity->project->db->transact([entity](auto * db) -> cppbase::Result { + CPPBASE_TRY(entity->project->db->execute( + "INSERT INTO `entities`(`name`) " + "SELECT `name` FROM `entities` WHERE `id` = ?", + entity->id + )); - TRY(entity->project->db->execute( - "INSERT INTO `entity_values`(`entity`, `name`, `value`) " - "SELECT ?1, `name`, `value` FROM `entity_values` WHERE " - "`entity` = ?1", - entity->id - )) + CPPBASE_TRY(entity->project->db->execute( + "INSERT INTO `entity_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 - )) + CPPBASE_TRY(entity->project->db->execute( + "INSERT INTO `entity_property_values`(`entity`, `property`, `value`) " + "SELECT ?1, `property`, `value` FROM `entity_property_values` " + "WHERE `entity` = ?1", + entity->id + )) - 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 - )) + CPPBASE_TRY(entity->project->db->execute( + "INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) " + "SELECT ?1, `property`, `value` FROM `entity_property_values` " + "WHERE `entity` = ?1", + entity->id + )) - return cppbase::ok(entity->project->db->last_insert_rowid()); - } - ) + return cppbase::ok(entity->project->db->last_insert_rowid()); + }) ); return new IkarusEntity{entity->project, id}; } -IkarusProject * -ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); +IkarusProject * ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); return entity->project; } -char const * -ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); +char const * ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto name, nullptr, "failed to get name for entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->query_one( - "SELECT `name` FROM `entities` WHERE `id` = ?", - entity->id - ) + entity->project->db->query_one("SELECT `name` FROM `entities` WHERE `id` = ?", entity->id) ); return name; @@ -134,33 +134,63 @@ void ikarus_entity_set_name( IkarusEntitySetNameFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN); IKARUS_TRYRV_OR_FAIL( IKARUS_VOID_RETURN, "failed to set name for entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->execute( - "UPDATE `entities` SET `name` = ? WHERE `id` = ?", - name, - entity->id - ) + entity->project->db->execute("UPDATE `entities` SET `name` = ? WHERE `id` = ?", name, entity->id) ); } -struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( +bool ikarus_entity_is_linked_to_blueprint( IkarusEntity * entity, - size_t * size_out, + struct IkarusBlueprint * blueprint, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); - IKARUS_FAIL_IF_NULL(size_out, nullptr); + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_ASCERTAIN( + false, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); - auto count = ikarus_entity_get_linked_blueprints_count(entity, error_out); - IKARUS_FAIL_IF_ERROR(nullptr); + IKARUS_FAIL_IF( + entity->project != blueprint->project, + false, + "blueprint does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); + + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check if entity is linked to blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT EXISTS(SELECT 1 FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?)", + entity->id, + blueprint->id + ) + ); + + return exists; +} + +struct IkarusBlueprint ** +ikarus_entity_get_linked_blueprints(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_VCALL(auto count, false, ikarus_entity_get_linked_blueprints_count, entity); std::int64_t ids[count]; @@ -189,22 +219,16 @@ struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( return blueprints; } -size_t ikarus_entity_get_linked_blueprints_count( - IkarusEntity * entity, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, 0); - IKARUS_FAIL_IF_NOT_EXIST(entity, 0); +size_t ikarus_entity_get_linked_blueprints_count(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto count, 0, "failed to get linked blueprints count for entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->query_one( - "SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", - entity->id - ) + entity->project->db + ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", entity->id) ); return count; @@ -216,10 +240,28 @@ void ikarus_entity_link_blueprint( IkarusEntityLinkBlueprintFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NULL(blueprint, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(blueprint, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_FAIL_IF( + entity->project != blueprint->project, + IKARUS_VOID_RETURN, + "blueprint does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); IKARUS_TRYRV_OR_FAIL( IKARUS_VOID_RETURN, @@ -239,10 +281,28 @@ void ikarus_entity_unlink_blueprint( IkarusEntityUnlinkBlueprintFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NULL(blueprint, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(blueprint, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_FAIL_IF( + entity->project != blueprint->project, + IKARUS_VOID_RETURN, + "blueprint does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); IKARUS_TRYRV_OR_FAIL( IKARUS_VOID_RETURN, @@ -256,13 +316,28 @@ void ikarus_entity_unlink_blueprint( ); } -IkarusEntityValue * ikarus_entity_get_values( - IkarusEntity * entity, - size_t * size_out, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); +bool ikarus_entity_has_value(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(name, false); + + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check if entity has value: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT EXISTS(SELECT 1 FROM `entity_values` WHERE `entity` = ? AND `name` = ?)", + entity->id, + name + ) + ); + + return exists; +} + +IkarusEntityValue * ikarus_entity_get_values(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto values_plain, @@ -277,17 +352,9 @@ IkarusEntityValue * ikarus_entity_get_values( IkarusEntityValue * values = new IkarusEntityValue[values_plain.size()]; - std::transform( - std::cbegin(values_plain), - std::cend(values_plain), - values, - [](auto const & tuple) { - return IkarusEntityValue{ - tuple.template get<0>(), - tuple.template get<1>() - }; - } - ); + std::transform(std::cbegin(values_plain), std::cend(values_plain), values, [](auto const & tuple) { + return IkarusEntityValue{tuple.template get<0>(), tuple.template get<1>()}; + }); if (size_out) { *size_out = values_plain.size(); @@ -296,15 +363,19 @@ IkarusEntityValue * ikarus_entity_get_values( return values; } -char const * ikarus_entity_get_value( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); +char const * ikarus_entity_get_value(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_ASCERTAIN( + nullptr, + "entity doesn't have value", + IkarusErrorInfo_Client_NotLinked, + ikarus_entity_has_value, + entity, + name + ); + IKARUS_VTRYRV_OR_FAIL( auto value, nullptr, @@ -326,10 +397,16 @@ void ikarus_entity_set_value( char const * value, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN); IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); // parsing from & to here to ensure values are valid JSON & formatted // uniformly @@ -338,7 +415,7 @@ void ikarus_entity_set_value( IKARUS_VOID_RETURN, "cannot parse value as JSON: {}", IkarusErrorInfo_Client_InvalidInput, - IkarusValue::from_json(value) + IkarusValue::from_json_str(value) ); IKARUS_TRYRV_OR_FAIL( @@ -361,29 +438,64 @@ void ikarus_entity_delete_value( IkarusEntityDeleteValueFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); + IKARUS_TRYRV_OR_FAIL( IKARUS_VOID_RETURN, "failed to delete value for entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->execute( - "DELETE FROM `entity_values` WHERE `entity` = ? AND `name` = ?", - entity->id, - name - ) + entity->project->db->execute("DELETE FROM `entity_values` WHERE `entity` = ? AND `name` = ?", entity->id, name) ); } -IkarusEntityPropertyValue * ikarus_entity_get_property_values( +bool ikarus_entity_has_property_value( IkarusEntity * entity, - size_t * size_out, + struct IkarusProperty * property, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + + IKARUS_ASCERTAIN( + false, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_FAIL_IF( + entity->project != property->project, + false, + "property does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); + + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check if entity has property value: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT EXISTS(SELECT 1 FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?)", + entity->id, + property->id + ) + ); + + return exists; +} + +IkarusEntityPropertyValue * +ikarus_entity_get_property_values(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto values_plain, @@ -391,24 +503,20 @@ IkarusEntityPropertyValue * ikarus_entity_get_property_values( "failed to get property values for entity: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_many( - "SELECT `property`, `value` FROM `entity_property_values` WHERE `entity` = ?", + "SELECT `e`.`property`, IFNULL(`e`.`value`, ikarus_default_value(`p`.`schema`)) FROM `entity_property_values` AS `e` " + "INNER JOIN `properties` AS `p` ON `p`.`id` = `e`.`property` " + " WHERE `e`.`entity` = ?", entity->id ) ); - IkarusEntityPropertyValue * values = - new IkarusEntityPropertyValue[values_plain.size()]; - std::transform( - std::cbegin(values_plain), - std::cend(values_plain), - values, - [entity](auto const & tuple) { - return IkarusEntityPropertyValue{ - new IkarusProperty{entity->project, tuple.template get<0>()}, - tuple.template get<1>() - }; - } - ); + IkarusEntityPropertyValue * values = new IkarusEntityPropertyValue[values_plain.size()]; + std::transform(std::cbegin(values_plain), std::cend(values_plain), values, [entity](auto const & tuple) { + return IkarusEntityPropertyValue{ + new IkarusProperty{entity->project, tuple.template get<0>()}, + tuple.template get<1>() + }; + }); if (size_out) { *size_out = values_plain.size(); @@ -417,15 +525,23 @@ IkarusEntityPropertyValue * ikarus_entity_get_property_values( return values; } -char const * ikarus_entity_get_property_value( - IkarusEntity * entity, - struct IkarusProperty * property, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); - IKARUS_FAIL_IF_NULL(property, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(property, nullptr); +char const * +ikarus_entity_get_property_value(IkarusEntity * entity, struct IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_ASCERTAIN( + false, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_FAIL_IF( + entity->project != property->project, + nullptr, + "property does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); IKARUS_VTRYRV_OR_FAIL( auto value, @@ -433,7 +549,9 @@ char const * ikarus_entity_get_property_value( "failed to get property value for entity: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_one( - "SELECT `value` FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?", + "SELECT IFNULL(`e`.`value`, ikarus_default_value(`p`.`schema`)) FROM `entity_property_values` AS `e` " + "INNER JOIN `properties` AS `p` ON `p`.`id` = `e`.`property` " + "WHERE `e`.`entity` = ? AND `e`.`property` = ?", entity->id, property->id ) @@ -449,12 +567,38 @@ void ikarus_entity_set_property_value( IkarusEntitySetPropertyValueFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NULL(property, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(property, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF( + entity->project != property->project, + IKARUS_VOID_RETURN, + "property does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity does not have property value", + IkarusErrorInfo_Client_NotLinked, + ikarus_entity_has_property_value, + entity, + property + ); + // parsing from & to here to ensure values are valid JSON & formatted // uniformly IKARUS_VTRYRV_OR_FAIL( @@ -462,7 +606,7 @@ void ikarus_entity_set_property_value( IKARUS_VOID_RETURN, "cannot parse value as JSON: {}", IkarusErrorInfo_Client_InvalidInput, - IkarusValue::from_json(value) + IkarusValue::from_json_str(value) ); IKARUS_TRYRV_OR_FAIL( @@ -478,3 +622,51 @@ void ikarus_entity_set_property_value( ) ); } + +void ikarus_entity_clear_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + IkarusEntitySetPropertyValueFlags flags, + IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_FAIL_IF( + entity->project != property->project, + IKARUS_VOID_RETURN, + "property does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't have property", + IkarusErrorInfo_Client_NotLinked, + ikarus_entity_has_property_value, + entity, + property + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to clear property value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "DELETE FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?", + entity->id, + property->id + ) + ); +} diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index e83b41a..50fb7f0 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -26,8 +26,7 @@ CREATE TABLE `properties` `blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE, `name` TEXT NOT NULL, `schema` TEXT NOT NULL, - `default_value` TEXT NOT NULL, - `settings` TEXT NOT NULL + `default_value` TEXT ) STRICT; CREATE TABLE `entity_blueprint_links` diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 75c8825..0028486 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -73,6 +73,49 @@ auto create_impl( .on_error(close_db) ); + db->create_function( + "ikarus_default_value", + 1, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + nullptr, + [](sqlite3_context * ctx, int argc, sqlite3_value ** argv) { + if (sqlite3_value_type(argv[0]) != SQLITE_TEXT) { + sqlite3_result_error( + ctx, + "expected the 'schema' parameter to be of type text", + SQLITE_MISMATCH + ); + return; + } + + auto const schema_json = + reinterpret_cast(sqlite3_value_text(argv[0])); + + auto schema_res = IkarusValueSchema::from_json_str(schema_json); + + if (schema_res.is_error()) { + sqlite3_result_error( + ctx, + "failed to parse schema", + SQLITE_ERROR + ); + return; + } + + auto schema = std::move(schema_res).unwrap_value(); + + auto default_value_json = + IkarusValue::to_json(schema.default_value()).dump(); + + sqlite3_result_text( + ctx, + default_value_json.data(), + default_value_json.size(), + SQLITE_TRANSIENT + ); + } + ); + return std::move(db); } diff --git a/src/ikarus/values/data.cpp b/src/ikarus/values/data.cpp index f7f6845..af9a1fc 100644 --- a/src/ikarus/values/data.cpp +++ b/src/ikarus/values/data.cpp @@ -20,7 +20,7 @@ auto get_primitive_type(IkarusValueDataPrimitive const & primitive) [](IkarusValueDataPrimitiveNumber const &) { return IkarusValuePrimitiveType_Number; }, - [](IkarusValueDataPrimitiveString const &) { + [](IkarusValueDataPrimitiveText const &) { return IkarusValuePrimitiveType_Text; } }, @@ -36,7 +36,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json) IkarusValueData value{}; - VTRY( + CPPBASE_VTRY( auto type, deserialize_enum( json, @@ -48,7 +48,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json) switch (type) { case IkarusValueDataType_Primitive: { - VTRY( + CPPBASE_VTRY( auto primitive, deserialize_enum( json, @@ -60,21 +60,21 @@ auto IkarusValueData::from_json(nlohmann::json const & json) switch (primitive) { case IkarusValuePrimitiveType_Toggle: { - VTRY(auto data, deserialize_any(json, "data")); + CPPBASE_VTRY(auto data, deserialize_any(json, "data")); value.variant = IkarusValueDataPrimitiveToggle{data}; break; } case IkarusValuePrimitiveType_Number: { - VTRY(auto data, deserialize_any(json, "data")); + CPPBASE_VTRY(auto data, deserialize_any(json, "data")); value.variant = IkarusValueDataPrimitiveNumber{data}; break; } case IkarusValuePrimitiveType_Text: { - VTRY(auto data, deserialize_any(json, "data")); + CPPBASE_VTRY(auto data, deserialize_any(json, "data")); - value.variant = IkarusValueDataPrimitiveString{data}; + value.variant = IkarusValueDataPrimitiveText{data}; break; } } @@ -83,14 +83,17 @@ auto IkarusValueData::from_json(nlohmann::json const & json) } case IkarusValueDataType_List: { std::vector> values_data{}; - VTRY( + CPPBASE_VTRY( auto data_json, deserialize_any>(json, "data") ); values_data.reserve(data_json.size()); for (auto const & data_json : data_json) { - VTRY(auto value_data, IkarusValueData::from_json(data_json)); + CPPBASE_VTRY( + auto value_data, + IkarusValueData::from_json(data_json) + ); values_data.emplace_back( cppbase::make_owning(std::move(value_data)) ); @@ -105,7 +108,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json) cppbase::owning_ptr>> map_data{}; - VTRY( + CPPBASE_VTRY( auto map_data_json, deserialize_any>(json, "data") ); @@ -113,10 +116,10 @@ auto IkarusValueData::from_json(nlohmann::json const & json) 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)); + CPPBASE_VTRY(auto key_json, get_key(pair_json, "key")); + CPPBASE_VTRY(auto value_json, get_key(pair_json, "value")); + CPPBASE_VTRY(auto key, IkarusValueData::from_json(*key_json)); + CPPBASE_VTRY(auto value, IkarusValueData::from_json(*value_json)); map_data.emplace_back( cppbase::make_owning(key), @@ -130,7 +133,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json) case IkarusValueDataType_Tuple: { std::vector> values_data{}; - VTRY( + CPPBASE_VTRY( auto values_json, deserialize_any>(json, "data") ); @@ -138,7 +141,10 @@ auto IkarusValueData::from_json(nlohmann::json const & json) values_data.reserve(values_json.size()); for (auto const & value_json : values_json) { - VTRY(auto value_data, IkarusValueData::from_json(value_json)); + CPPBASE_VTRY( + auto value_data, + IkarusValueData::from_json(value_json) + ); values_data.emplace_back( cppbase::make_owning(value_data) ); @@ -152,6 +158,19 @@ auto IkarusValueData::from_json(nlohmann::json const & json) return cppbase::ok(value); } +auto IkarusValueData::from_json_str(std::string_view json_str) + -> cppbase::Result { + auto json = nlohmann::json::parse(json_str, nullptr, false); + + if (json.is_discarded()) { + return cppbase::err( + IkarusValueDataParseError{IkarusJsonError{IkarusJsonParseError{}}} + ); + } + + return IkarusValueData::from_json(json); +} + auto IkarusValueData::to_json(IkarusValueData const & value) -> nlohmann::json { nlohmann::json json = nlohmann::json::object(); @@ -170,7 +189,7 @@ auto IkarusValueData::to_json(IkarusValueData const & value) -> nlohmann::json { json["primitive"] = IkarusValuePrimitiveType_Number; json["data"] = number.value; }, - [&](IkarusValueDataPrimitiveString const & string) { + [&](IkarusValueDataPrimitiveText const & string) { json["type"] = IkarusValueDataType_Primitive; json["primitive"] = IkarusValuePrimitiveType_Text; json["data"] = string.value; diff --git a/src/ikarus/values/data.hpp b/src/ikarus/values/data.hpp index 1c9b3d8..2e68949 100644 --- a/src/ikarus/values/data.hpp +++ b/src/ikarus/values/data.hpp @@ -22,14 +22,14 @@ struct IkarusValueDataPrimitiveNumber { double value; }; -struct IkarusValueDataPrimitiveString { +struct IkarusValueDataPrimitiveText { std::string value; }; using IkarusValueDataPrimitive = std::variant< IkarusValueDataPrimitiveToggle, IkarusValueDataPrimitiveNumber, - IkarusValueDataPrimitiveString>; + IkarusValueDataPrimitiveText>; struct IkarusValueDataList { std::vector> values; @@ -53,7 +53,9 @@ struct IkarusValueData { IkarusValueDataMap, IkarusValueDataTuple>; - static auto from_json(nlohmann::json const & json) + static auto from_json(nlohmann::json const & json_str) + -> cppbase::Result; + static auto from_json_str(std::string_view json_str) -> cppbase::Result; static auto to_json(IkarusValueData const & value) -> nlohmann::json; diff --git a/src/ikarus/values/errors.hpp b/src/ikarus/values/errors.hpp index f472d83..1b8a880 100644 --- a/src/ikarus/values/errors.hpp +++ b/src/ikarus/values/errors.hpp @@ -10,12 +10,15 @@ struct IkarusJsonInvalidTypeError {}; struct IkarusJsonEnumOutOfBoundsError {}; +struct IkarusJsonParseError {}; + struct IkarusJsonUnknownError {}; using IkarusJsonError = std::variant< IkarusJsonMissingKeyError, IkarusJsonInvalidTypeError, IkarusJsonEnumOutOfBoundsError, + IkarusJsonParseError, IkarusJsonUnknownError>; struct IkarusValueSchemaParseError { @@ -57,6 +60,16 @@ struct fmt::formatter : formatter { } }; +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusJsonParseError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "buffer isn't valid JSON"); + } +}; + template<> struct fmt::formatter : formatter { constexpr static auto format( diff --git a/src/ikarus/values/schema.cpp b/src/ikarus/values/schema.cpp index ea2e6dc..3c53fe3 100644 --- a/src/ikarus/values/schema.cpp +++ b/src/ikarus/values/schema.cpp @@ -5,6 +5,7 @@ #include #include #include +#include auto IkarusValueSchema::from_json(nlohmann::json const & json) -> cppbase::Result { @@ -18,7 +19,7 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) IkarusValueSchema schema{}; - VTRY( + CPPBASE_VTRY( auto type, deserialize_enum( json, @@ -30,7 +31,7 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) switch (type) { case IkarusValueSchemaType_Primitive: { - VTRY( + CPPBASE_VTRY( auto primitive, deserialize_enum( json, @@ -43,15 +44,21 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) break; } case IkarusValueSchemaType_List: { - VTRY(auto sub_schema, IkarusValueSchema::from_json(json["schema"])); + CPPBASE_VTRY( + auto sub_schema, + IkarusValueSchema::from_json(json["schema"]) + ); schema.variant = IkarusValueSchemaList{ cppbase::make_owning(std::move(sub_schema)) }; break; } case IkarusValueSchemaType_Map: { - VTRY(auto key_schema, IkarusValueSchema::from_json(json["key_schema"])); - VTRY( + CPPBASE_VTRY( + auto key_schema, + IkarusValueSchema::from_json(json["key_schema"]) + ); + CPPBASE_VTRY( auto value_schema, IkarusValueSchema::from_json(json["value_schema"]) ); @@ -62,7 +69,7 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) break; } case IkarusValueSchemaType_Tuple: { - VTRY( + CPPBASE_VTRY( auto sub_schemas_json, deserialize_any>(json, "schemas") ); @@ -71,7 +78,10 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) 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)); + CPPBASE_VTRY( + auto schema, + IkarusValueSchema::from_json(sub_schema_json) + ); sub_schemas.emplace_back( cppbase::make_owning(std::move(schema)) ); @@ -85,6 +95,19 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) return cppbase::ok(std::move(schema)); } +auto IkarusValueSchema::from_json_str(std::string_view json_str) + -> cppbase::Result { + auto json = nlohmann::json::parse(json_str, nullptr, false); + + if (json.is_discarded()) { + return cppbase::err( + IkarusValueSchemaParseError{IkarusJsonError{IkarusJsonParseError{}}} + ); + } + + return IkarusValueSchema::from_json(json); +} + auto IkarusValueSchema::to_json(IkarusValueSchema const & schema) -> nlohmann::json { nlohmann::json json = nlohmann::json::object(); @@ -173,3 +196,46 @@ auto IkarusValueSchema::validate(IkarusValueData const & data) const -> bool { data.variant ); } + +auto IkarusValueSchema::default_value_data() const -> IkarusValueData { + return std::visit( + cppbase::overloaded{ + [](IkarusValueSchemaPrimitive const & schema) -> IkarusValueData { + switch (schema.type) { + case IkarusValuePrimitiveType_Toggle: { + return {{IkarusValueDataPrimitiveToggle{false}}}; + } + case IkarusValuePrimitiveType_Number: + return {{IkarusValueDataPrimitiveNumber{0.0}}}; + case IkarusValuePrimitiveType_Text: + return {{IkarusValueDataPrimitiveText{""}}}; + } + }, + [](IkarusValueSchemaList const & schema) -> IkarusValueData { + return {IkarusValueDataList{{}}}; + }, + [](IkarusValueSchemaMap const & schema) -> IkarusValueData { + return {IkarusValueDataMap{{}}}; + }, + [](IkarusValueSchemaTuple const & schema) -> IkarusValueData { + IkarusValueDataTuple data{}; + data.values.reserve(schema.sub_schemas.size()); + + for (auto const & sub_schema : schema.sub_schemas) { + data.values.emplace_back( + cppbase::make_owning( + sub_schema->default_value().data + ) + ); + } + + return {data}; + } + }, + variant + ); +} + +auto IkarusValueSchema::default_value() const -> IkarusValue { + return IkarusValue{*this, default_value_data()}; +} diff --git a/src/ikarus/values/schema.hpp b/src/ikarus/values/schema.hpp index 7fd07ab..e8df649 100644 --- a/src/ikarus/values/schema.hpp +++ b/src/ikarus/values/schema.hpp @@ -42,9 +42,13 @@ struct IkarusValueSchema { static auto from_json(nlohmann::json const & json) -> cppbase::Result; + static auto from_json_str(std::string_view json_str) + -> cppbase::Result; static auto to_json(IkarusValueSchema const & value) -> nlohmann::json; auto validate(IkarusValueData const & data) const -> bool; + auto default_value_data() const -> IkarusValueData; + auto default_value() const -> IkarusValue; IkarusValueSchemaVariant variant; }; diff --git a/src/ikarus/values/shared.hpp b/src/ikarus/values/shared.hpp index a4d50ac..15e6753 100644 --- a/src/ikarus/values/shared.hpp +++ b/src/ikarus/values/shared.hpp @@ -32,7 +32,7 @@ auto deserialize_enum( E min, E max ) -> cppbase::Result { - VTRY(auto iter, get_key(json, key)); + CPPBASE_VTRY(auto iter, get_key(json, key)); if (!iter->is_number_integer()) { return cppbase::err(IkarusJsonError{}); @@ -51,7 +51,7 @@ auto deserialize_enum( template auto deserialize_any(nlohmann::json const & json, std::string_view key) -> cppbase::Result { - VTRY(auto iter, get_key(json, key)); + CPPBASE_VTRY(auto iter, get_key(json, key)); try { return cppbase::ok(iter->get()); diff --git a/src/ikarus/values/value.cpp b/src/ikarus/values/value.cpp index 9a23a98..304e656 100644 --- a/src/ikarus/values/value.cpp +++ b/src/ikarus/values/value.cpp @@ -15,8 +15,8 @@ auto IkarusValue::from_json(nlohmann::json const & json) IkarusValue value{}; - VTRY(value.schema, IkarusValueSchema::from_json(json["schema"])); - VTRY(value.data, IkarusValueData::from_json(json["data"])); + CPPBASE_VTRY(value.schema, IkarusValueSchema::from_json(json["schema"])); + CPPBASE_VTRY(value.data, IkarusValueData::from_json(json["data"])); if (!value.schema.validate(value.data)) { return cppbase::err(IkarusValueParseErrorDataSchemaMismatch{}); @@ -25,6 +25,19 @@ auto IkarusValue::from_json(nlohmann::json const & json) return cppbase::ok(std::move(value)); } +auto IkarusValue::from_json_str(std::string_view json_str) + -> cppbase::Result { + auto json = nlohmann::json::parse(json_str, nullptr, false); + + if (json.is_discarded()) { + return cppbase::err( + IkarusValueDataParseError{IkarusJsonError{IkarusJsonParseError{}}} + ); + } + + return IkarusValue::from_json(json); +} + auto IkarusValue::to_json(IkarusValue const & value) -> nlohmann::json { nlohmann::json json = nlohmann::json::object(); diff --git a/src/ikarus/values/value.hpp b/src/ikarus/values/value.hpp index 74db914..cb70b1f 100644 --- a/src/ikarus/values/value.hpp +++ b/src/ikarus/values/value.hpp @@ -14,5 +14,7 @@ struct IkarusValue { static auto from_json(nlohmann::json const & json) -> cppbase::Result; + static auto from_json_str(std::string_view json_str) + -> cppbase::Result; static auto to_json(IkarusValue const & value) -> nlohmann::json; }; diff --git a/vendor/cppbase b/vendor/cppbase index e7741e0..82da677 160000 --- a/vendor/cppbase +++ b/vendor/cppbase @@ -1 +1 @@ -Subproject commit e7741e0e60f380f8b473c0881f3d8b4f23d0df91 +Subproject commit 82da6775fc56649cff6e611d6826a8e8fd321baa diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 5bfd32f..5fd5731 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 5bfd32f66164ed3d2c28003139fc5f76ef5e206d +Subproject commit 5fd5731c98d6e260e35d05a59c99822e32fe7191 From 71964229e709ea0ebe73d71d1d16c81501bcce47 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 4 Jan 2025 15:25:10 +0100 Subject: [PATCH 073/166] add blueprint & property implementation --- include/ikarus/objects/blueprint.h | 28 +- include/ikarus/objects/property.h | 109 +++++- src/ikarus/objects/blueprint.cpp | 329 +++++++++++++++++- src/ikarus/objects/entity.cpp | 36 +- src/ikarus/objects/property.cpp | 307 ++++++++++++++++ .../migrations/m0_initial_layout.sql | 6 +- 6 files changed, 772 insertions(+), 43 deletions(-) diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 7df09a8..0631126 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -154,6 +154,22 @@ IKA_API void ikarus_blueprint_set_name( struct IkarusErrorData * error_out ); +/// \brief Gets whether a blueprint has a property. +/// \param blueprint The blueprint to check the property of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to check the existence of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \pre \li Must be in the same project as the blueprint. +/// \param error_out \see errors.h +/// \return True if the blueprint has the property, false otherwise or if an error occurs. +IKA_API bool ikarus_blueprint_has_property( + struct IkarusBlueprint * blueprint, + struct IkarusProperty * property, + struct IkarusErrorData * error_out +); + /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. /// \pre \li Must not be null. @@ -161,7 +177,7 @@ IKA_API void ikarus_blueprint_set_name( /// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs. /// \param error_out \see errors.h /// \return The properties of the blueprint or null if an error occurs. -IKA_API struct IkarusBlueprint ** ikarus_blueprint_get_properties( +IKA_API struct IkarusProperty ** ikarus_blueprint_get_properties( struct IkarusBlueprint * blueprint, size_t * size_out, struct IkarusErrorData * error_out @@ -176,15 +192,15 @@ IKA_API struct IkarusBlueprint ** ikarus_blueprint_get_properties( IKA_API size_t ikarus_blueprint_get_properties_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); -/// \brief Gets all entities linked to a blueprint. -/// \param blueprint The blueprint to get the entities of. +/// \brief Gets the linked entities of a blueprint. +/// \param blueprint The blueprint to get the linked entities of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs. /// \remark Ignore if null. /// \param error_out \see errors.h -/// \return The entities linked to the blueprint or null if an error occurs. -IKA_API struct IkarusEntity ** ikarus_blueprint_get_entities( +/// \return The linked entities of the blueprint or null if an error occurs. +IKA_API struct IkarusEntity ** ikarus_blueprint_get_linked_entities( struct IkarusBlueprint * blueprint, size_t * size_out, struct IkarusErrorData * error_out @@ -197,7 +213,7 @@ IKA_API struct IkarusEntity ** ikarus_blueprint_get_entities( /// \param error_out \see errors.h /// \return The number of entities linked to the blueprint or 0 if an error occurs. IKA_API size_t -ikarus_blueprint_get_entities_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); +ikarus_blueprint_get_linked_entities_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index 2124423..0a6c3d1 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -53,23 +53,47 @@ enum IkarusPropertyCreateFlags { /// \param project The project to create the property in. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param blueprint The blueprint to create the property for. +/// \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. +/// \pre \li Must be a valid JSON buffer for a IkarusValueSchema. \see schema.h +/// \param default_value The default value of the property. +/// \pre \li Must not be null. +/// \pre \li Must be a valid JSON buffer for an IkarusValueData. \see data.h /// \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, + struct IkarusBlueprint * blueprint, char const * name, - struct IkarusValueSchema * schema, + char const * schema, + char const * default_value, IkarusPropertyCreateFlags flags, IkarusErrorData * error_out ); +/// \brief Flags for copying a property. +enum IkarusPropertyCopyFlags { + /// \brief No flags. + IkarusPropertyCopyFlags_None = 0, +}; + +/// \brief Copy a property. +/// \param property The property to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param flags Flags for copying the property. +/// \param error_out \see errors.h +/// \return The copied property or NULL if an error occurred. +IKA_API IkarusProperty * +ikarus_property_copy(IkarusProperty * property, IkarusPropertyCopyFlags flags, IkarusErrorData * error_out); + /// \brief Flags for deleting a property. enum IkarusPropertyDeleteFlags { /// \brief No flags. @@ -101,15 +125,6 @@ IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty * prop /// \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. @@ -133,6 +148,78 @@ IKA_API void ikarus_property_set_name( 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 in JSON format or null if an error occurred. \see schema.h +IKA_API char const * ikarus_property_get_schema(IkarusProperty * property, IkarusErrorData * error_out); + +/// \brief Flags for setting the schema of a property. +enum IkarusPropertySetSchemaFlags { + /// \brief No flags. + IkarusPropertySetSchemaFlags_None = 0, +}; + +/// \brief Set the schema of a property. +/// \details Setting the schema of a property will reset all existing values. +/// \param property The property to set the schema of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param schema The new schema of the property. +/// \pre \li Must not be null. +/// \pre \li Must be a valid JSON buffer for a IkarusValueSchema. \see schema.h +/// \param new_default_value The new default value of the property. +/// \pre \li Must not be null. +/// \pre \li Must be a valid JSON buffer for an IkarusValueData. \see data.h +/// \param flags Flags for setting the schema of the property. +/// \param error_out \see errors.h +IKA_API void ikarus_property_set_schema( + IkarusProperty * property, + char const * schema, + char const * new_default_value, + IkarusPropertySetSchemaFlags flags, + IkarusErrorData * error_out +); + +/// \brief Get the default value of a property. +/// \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 data of the property in JSON format or null if an error occurred. \see data.h +IKA_API char const * ikarus_property_get_default_value(IkarusProperty * property, IkarusErrorData * error_out); + +/// \details The default value must match the schema of the property. + +/// \brief Flags for setting the default value of a property. +enum IkarusPropertySetDefaultValueFlags { + /// \brief No flags. + IkarusPropertySetDefaultValueFlags_None = 0, +}; + +/// \brief Flags for setting the default value of a property. +/// \param property The property to set the default value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param value The new default value of the property. +/// \pre \li Must not be null. +/// \pre \li Must be valid JSON \see data.h +/// \pre \li Must be valid according to the property's schema. +/// \param flags Flags for setting the default value of the property. +/// \param error_out \see errors.h +IKA_API void ikarus_property_set_default_value( + IkarusProperty * property, + char const * value, + IkarusPropertySetDefaultValueFlags flags, + IkarusErrorData * error_out +); + +/// \brief Gets all values for a property. +/// \param property The property to get the values of. +/// + IKARUS_END_HEADER /// @} diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index ba9e7b1..e02e07f 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -3,12 +3,27 @@ #include #include #include +#include #include IkarusBlueprint::IkarusBlueprint(struct IkarusProject * project, int64_t id): project{project}, id{id} {} +bool ikarus_blueprint_exists(IkarusBlueprint * blueprint, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(blueprint, false); + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check whether blueprint exists: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db + ->query_one("SELECT EXISTS(SELECT 1 FROM `blueprints` WHERE `id` = ?)", blueprint->id) + ); + + return exists; +} + IkarusBlueprint * ikarus_blueprint_create( struct IkarusProject * project, char const * name, @@ -35,17 +50,14 @@ IkarusBlueprint * ikarus_blueprint_create_from_entity( IkarusBlueprintCreateFromEntityFlags flags, struct IkarusErrorData * error_out ) { - IKARUS_TRYRV_OR_FAIL( - nullptr, - "{}", - IkarusErrorInfo_Database_QueryFailed, - ikarus_libikarus_func_call_to_result( - error_out, - ikarus_must_return_true(ikarus_entity_exists, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent), - entity - ) - ); IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + IKARUS_ASCERTAIN( + nullptr, + "entity must not be null", + IkarusErrorInfo_Client_InvalidInput, + ikarus_entity_exists, + entity + ); IKARUS_VTRYRV_OR_FAIL( auto id, @@ -59,15 +71,308 @@ IkarusBlueprint * ikarus_blueprint_create_from_entity( CPPBASE_TRY(entity->project->db->execute( "INSERT INTO `properties`(`blueprint`, `name`, `schema`) " - "SELECT ?, `name`, json_extract(`value`, '$.schema') FROM `entity_values` " + "SELECT ?, `name`, json_extract(`value`, '$.schema') AS `schema` FROM `entity_values` " "WHERE `entity` = ?", id, entity->id )) - return cppbase::ok(entity->project->db->last_insert_rowid()); + return cppbase::ok(id); }) ); return new IkarusBlueprint{entity->project, id}; } + +struct IkarusBlueprint * ikarus_blueprint_copy( + struct IkarusBlueprint * blueprint, + IkarusBlueprintCopyFlags flags, + struct IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VTRYRV_OR_FAIL( + auto id, + nullptr, + "failed to copy blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->transact( + [blueprint](auto * db) -> cppbase::Result { + CPPBASE_TRY(db->execute( + "INSERT INTO `blueprints`(`name`) " + "SELECT `name` FROM `blueprints` WHERE `id` = ?", + blueprint->id + )); + + auto const id = db->last_insert_rowid(); + + CPPBASE_TRY(blueprint->project->db->execute( + "INSERT INTO `properties`(`blueprint`, `name`, `schema`) " + "SELECT ?, `name`, `schema` FROM `properties` " + "WHERE `blueprint` = ?", + id, + blueprint->id + )); + + return cppbase::ok(id); + } + ) + ); + + return new IkarusBlueprint{blueprint->project, id}; +} + +void ikarus_blueprint_delete( + struct IkarusBlueprint * blueprint, + IkarusBlueprintDeleteFlags flags, + IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to delete blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->execute("DELETE FROM `blueprints` WHERE `id` = ?", blueprint->id) + ); + + delete blueprint; +} + +struct IkarusProject * +ikarus_blueprint_get_project(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + return blueprint->project; +} + +char const * ikarus_blueprint_get_name(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VTRYRV_OR_FAIL( + auto name, + nullptr, + "failed to get name for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_one("SELECT `name` FROM `blueprints` WHERE `id` = ?", blueprint->id) + ); + + return name; +} + +void ikarus_blueprint_set_name( + struct IkarusBlueprint * blueprint, + char const * name, + IkarusBlueprintSetNameFlags flags, + struct IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set name for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->execute("UPDATE `blueprints` SET `name` = ? WHERE `id` = ?", name, blueprint->id) + ); +} + +bool ikarus_blueprint_has_property( + struct IkarusBlueprint * blueprint, + struct IkarusProperty * property, + struct IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + false, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_ASCERTAIN( + false, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_FAIL_IF( + blueprint->project != property->project, + false, + "property does not belong to blueprint's project", + IkarusErrorInfo_Client_NotLinked + ); + + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check if blueprint has property: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_one( + "SELECT EXISTS(SELECT 1 FROM `properties` WHERE `blueprint` = ? AND `id` = ?)", + blueprint->id, + property->id + ) + ); + + return exists; +} + +struct IkarusProperty ** ikarus_blueprint_get_properties( + struct IkarusBlueprint * blueprint, + size_t * size_out, + struct IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VCALL(auto count, nullptr, ikarus_blueprint_get_properties_count, blueprint); + + std::int64_t ids[count]; + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to get properties for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_many_buffered( + "SELECT `id` FROM `properties` WHERE `blueprint` = ?", + ids, + count, + blueprint->id + ) + ); + + auto properties = new IkarusProperty *[count]; + std::transform(ids, ids + count, properties, [project = blueprint->project](auto id) { + return new IkarusProperty{project, id}; + }); + + if (size_out) { + *size_out = count; + } + + return properties; +} + +size_t ikarus_blueprint_get_properties_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + 0, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VTRYRV_OR_FAIL( + auto count, + 0, + "failed to get properties count for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db + ->query_one("SELECT COUNT(*) FROM `properties` WHERE `blueprint` = ?", blueprint->id) + ); + + return count; +} + +struct IkarusEntity ** ikarus_blueprint_get_linked_entities( + struct IkarusBlueprint * blueprint, + size_t * size_out, + struct IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VCALL(auto count, nullptr, ikarus_blueprint_get_linked_entities_count, blueprint); + + std::int64_t ids[count]; + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to get entities for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_many_buffered( + "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", + ids, + count, + blueprint->id + ) + ); + + auto entities = new IkarusEntity *[count]; + std::transform(ids, ids + count, entities, [project = blueprint->project](auto id) { + return new IkarusEntity{project, id}; + }); + + if (size_out) { + *size_out = count; + } + + return entities; +} + +size_t +ikarus_blueprint_get_linked_entities_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + 0, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VTRYRV_OR_FAIL( + auto count, + 0, + "failed to get linked entities count for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_one( + "SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?", + blueprint->id + ) + ); + + return count; +} diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index aae7564..ad61368 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -80,6 +80,8 @@ IkarusEntity * ikarus_entity_copy(IkarusEntity * entity, IkarusEntityCopyFlags f entity->id )); + auto const id = entity->project->db->last_insert_rowid(); + CPPBASE_TRY(entity->project->db->execute( "INSERT INTO `entity_values`(`entity`, `name`, `value`) " "SELECT ?1, `name`, `value` FROM `entity_values` WHERE " @@ -101,7 +103,7 @@ IkarusEntity * ikarus_entity_copy(IkarusEntity * entity, IkarusEntityCopyFlags f entity->id )) - return cppbase::ok(entity->project->db->last_insert_rowid()); + return cppbase::ok(id); }) ); @@ -189,8 +191,8 @@ bool ikarus_entity_is_linked_to_blueprint( struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) { - IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); - IKARUS_VCALL(auto count, false, ikarus_entity_get_linked_blueprints_count, entity); + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_VCALL(auto count, nullptr, ikarus_entity_get_linked_blueprints_count, entity); std::int64_t ids[count]; @@ -495,7 +497,7 @@ bool ikarus_entity_has_property_value( IkarusEntityPropertyValue * ikarus_entity_get_property_values(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) { - IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto values_plain, @@ -503,9 +505,12 @@ ikarus_entity_get_property_values(IkarusEntity * entity, size_t * size_out, Ikar "failed to get property values for entity: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_many( - "SELECT `e`.`property`, IFNULL(`e`.`value`, ikarus_default_value(`p`.`schema`)) FROM `entity_property_values` AS `e` " - "INNER JOIN `properties` AS `p` ON `p`.`id` = `e`.`property` " - " WHERE `e`.`entity` = ?", + "SELECT `p`.`id`, IFNULL(`v`.`value`, ikarus_default_value(`p`.`schema`)) " + "FROM `entities` AS `e` " + "INNER JOIN `entity_blueprint_links` as `l` ON `l`.`entity` = `e`.`id` " + "INNER JOIN `properties` AS `p` ON `p`.`blueprint` = `l`.`blueprint` " + "LEFT JOIN `entity_property_values` AS `v` ON `e`.`entity` = `e`.`id` " + "WHERE `e`.`entity` = ?", entity->id ) ); @@ -527,9 +532,9 @@ ikarus_entity_get_property_values(IkarusEntity * entity, size_t * size_out, Ikar char const * ikarus_entity_get_property_value(IkarusEntity * entity, struct IkarusProperty * property, IkarusErrorData * error_out) { - IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_ASCERTAIN( - false, + nullptr, "property doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_property_exists, @@ -543,6 +548,15 @@ ikarus_entity_get_property_value(IkarusEntity * entity, struct IkarusProperty * IkarusErrorInfo_Client_NotLinked ); + IKARUS_ASCERTAIN( + nullptr, + "entity doesn't have property value", + IkarusErrorInfo_Client_NotLinked, + ikarus_entity_has_property_value, + entity, + property + ); + IKARUS_VTRYRV_OR_FAIL( auto value, nullptr, @@ -567,6 +581,8 @@ void ikarus_entity_set_property_value( IkarusEntitySetPropertyValueFlags flags, IkarusErrorData * error_out ) { + IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( IKARUS_VOID_RETURN, "entity doesn't exist", @@ -581,7 +597,6 @@ void ikarus_entity_set_property_value( ikarus_property_exists, property ); - IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); IKARUS_FAIL_IF( entity->project != property->project, @@ -650,6 +665,7 @@ void ikarus_entity_clear_property_value( "property does not belong to entity's project", IkarusErrorInfo_Client_NotLinked ); + IKARUS_ASCERTAIN( IKARUS_VOID_RETURN, "entity doesn't have property", diff --git a/src/ikarus/objects/property.cpp b/src/ikarus/objects/property.cpp index 43fac96..f6e8eb7 100644 --- a/src/ikarus/objects/property.cpp +++ b/src/ikarus/objects/property.cpp @@ -2,9 +2,316 @@ #include #include +#include #include #include IkarusProperty::IkarusProperty(struct IkarusProject * project, int64_t id): project{project}, id{id} {} + +bool ikarus_property_exists(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(property, false); + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check whether property exists: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->query_one("SELECT EXISTS(SELECT 1 FROM `properties` WHERE `id` = ?)", property->id) + ); + + return exists; +} + +IkarusProperty * ikarus_property_create( + struct IkarusBlueprint * blueprint, + char const * name, + char const * schema, + char const * default_value, + IkarusPropertyCreateFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(blueprint, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VTRYRV_OR_FAIL( + auto schema_parsed, + nullptr, + "cannot parse schema as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValueSchema::from_json_str(schema) + ); + + IKARUS_VTRYRV_OR_FAIL( + auto default_value_parsed, + nullptr, + "cannot parse default value as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValueData::from_json_str(default_value) + ); + + IKARUS_FAIL_IF( + !schema_parsed.validate(default_value_parsed), + nullptr, + "default value is invalid for schema", + IkarusErrorInfo_Client_InvalidInput + ); + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to create property: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->execute( + "INSERT INTO `properties`(`blueprint`, `name`, `schema`, `default_value`) VALUES(?, ?, ?, ?)", + blueprint->id, + name, + IkarusValueSchema::to_json(schema_parsed).dump(), + IkarusValueData::to_json(default_value_parsed).dump() + ) + ); + + auto const id = blueprint->project->db->last_insert_rowid(); + return new IkarusProperty{blueprint->project, id}; +} + +IkarusProperty * +ikarus_property_copy(IkarusProperty * property, IkarusPropertyCopyFlags flags, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to copy property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->execute( + "INSERT INTO `properties`(`blueprint`, `name`, `schema`, `default_value`) " + "SELECT `blueprint`, `name`, `schema`, `default_value` FROM `properties` WHERE `id` = ?", + property->id + ) + ); + + auto const id = property->project->db->last_insert_rowid(); + return new IkarusProperty{property->project, id}; +} + +void ikarus_property_delete(IkarusProperty * property, IkarusPropertyDeleteFlags flags, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to delete property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->execute("DELETE FROM `properties` WHERE `id` = ?", property->id) + ); + + delete property; +} + +struct IkarusProject * ikarus_property_get_project(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + return property->project; +} + +char const * ikarus_property_get_name(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_VTRYRV_OR_FAIL( + auto name, + nullptr, + "failed to get name for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->query_one("SELECT `name` FROM `properties` WHERE `id` = ?", property->id) + ); + + return name; +} + +void ikarus_property_set_name( + IkarusProperty * property, + char const * name, + IkarusPropertySetNameFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set name for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->execute("UPDATE `properties` SET `name` = ? WHERE `id` = ?", name, property->id) + ); +} + +char const * ikarus_property_get_schema(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_VTRYRV_OR_FAIL( + auto schema, + nullptr, + "failed to get schema for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->query_one("SELECT `schema` FROM `properties` WHERE `id` = ?", property->id) + ); + + return schema; +} + +void ikarus_property_set_schema( + IkarusProperty * property, + char const * schema, + char const * new_default_value, + IkarusPropertySetSchemaFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(schema, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(new_default_value, IKARUS_VOID_RETURN); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_VTRYRV_OR_FAIL( + auto schema_parsed, + IKARUS_VOID_RETURN, + "cannot parse schema as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValueSchema::from_json_str(schema) + ); + + IKARUS_VTRYRV_OR_FAIL( + auto default_value_parsed, + IKARUS_VOID_RETURN, + "cannot parse default value as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValueData::from_json_str(new_default_value) + ); + + IKARUS_FAIL_IF( + !schema_parsed.validate(default_value_parsed), + IKARUS_VOID_RETURN, + "default value is invalid for schema", + IkarusErrorInfo_Client_InvalidInput + ); + + // transact updating the property and all of its associated values + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set schema for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->transact([&](auto * db) -> cppbase::Result { + CPPBASE_TRY(db->execute( + "UPDATE `properties` SET `schema` = ? WHERE `id` = ?", + IkarusValueSchema::to_json(schema_parsed).dump(), + property->id + )); + + CPPBASE_TRY(db->execute( + "UPDATE `entity_property_values` SET `value` = ? WHERE `property` = ?", + IkarusValueData::to_json(default_value_parsed).dump(), + property->id + )); + + return cppbase::ok(); + }) + ); +} + +char const * ikarus_property_get_default_value(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_VTRYRV_OR_FAIL( + auto default_value, + nullptr, + "failed to get default value for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db + ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id) + ); + + return default_value; +} + +void ikarus_property_set_default_value( + IkarusProperty * property, + char const * value, + IkarusPropertySetDefaultValueFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_VTRYRV_OR_FAIL( + auto value_parsed, + IKARUS_VOID_RETURN, + "cannot parse value as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValueData::from_json_str(value) + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set default value for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->execute( + "UPDATE `properties` SET `default_value` = ? WHERE `id` = ?", + IkarusValueData::to_json(value_parsed).dump(), + property->id + ) + ); +} diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index 50fb7f0..408eda1 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -10,8 +10,7 @@ CREATE TABLE `entity_values` `name` TEXT NOT NULL, `value` TEXT NOT NULL, - PRIMARY KEY (`id`), - UNIQUE (`entity`, `name`) + PRIMARY KEY (`entity`, `name`) ) STRICT; CREATE TABLE `blueprints` @@ -44,6 +43,5 @@ CREATE TABLE `entity_property_values` `property` INTEGER NOT NULL REFERENCES `properties` (`id`) ON DELETE CASCADE, `value` TEXT NOT NULL, - PRIMARY KEY (`id`), - UNIQUE (`entity`, `property`) + PRIMARY KEY (`entity`, `property`) ) STRICT; From f0ad11f9aee1b507aec8c01cb9599be3b70dc364 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 10:31:07 +0100 Subject: [PATCH 074/166] rename to libikarus and remove default cmake prefix --- CMakeLists.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 63b949c..a007d4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,13 +12,14 @@ add_subdirectory(src) find_package(Boost CONFIG COMPONENTS system filesystem REQUIRED) add_library( - ikarus SHARED + libikarus SHARED ${INCLUDE_FILES} ${SOURCE_FILES} ) set_target_properties( - ikarus PROPERTIES + libikarus PROPERTIES + PREFIX "" CXX_STANDARD 23 CXX_STANDARD_REQUIRED ON LINKER_LANGUAGE CXX @@ -26,7 +27,7 @@ set_target_properties( ) target_include_directories( - ikarus + libikarus PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include PRIVATE @@ -35,7 +36,7 @@ target_include_directories( ) target_link_libraries( - ikarus PRIVATE + libikarus PRIVATE cppbase sqlitecpp nlohmann_json::nlohmann_json @@ -46,9 +47,9 @@ if (LIBIKARUS_ENABLE_LINTS) find_program(IWYU_PATH NAMES include-what-you-use iwyu REQUIRED) find_program(CLANG_TIDY_PATH NAMES clang-tidy REQUIRED) - set_property(TARGET ikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) + set_property(TARGET libikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) set_property( - TARGET ikarus + TARGET libikarus PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY_PATH}; ) endif () @@ -64,7 +65,7 @@ if (LIBIKARUS_BUILD_DOCS) ) add_dependencies( - ikarus + libikarus libikarus_docs ) endif () From 0590959f7c4f1d46a3bc4da038394be86852d79d Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 12:15:07 +0100 Subject: [PATCH 075/166] check result from 'ikarus_default_value' SQLite function creation --- src/ikarus/persistence/project.cpp | 176 ++++++++--------------------- 1 file changed, 49 insertions(+), 127 deletions(-) diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 0028486..692e8f8 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -18,11 +18,8 @@ constexpr char const * DB_PROJECT_ID_KEY = "project_id"; constexpr char const * DB_PROJECT_NAME_KEY = "project_name"; -auto create_impl( - std::string_view path, - std::string_view name, - IkarusErrorData * error_out -) -> std::unique_ptr { +auto create_impl(std::string_view path, std::string_view name, IkarusErrorData * error_out) + -> std::unique_ptr { IKARUS_VTRYRV_OR_FAIL( auto db, nullptr, @@ -65,65 +62,46 @@ auto create_impl( 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) + db->execute("INSERT INTO `metadata`(`key`, `value`) VALUES(?, ?)", DB_PROJECT_NAME_KEY, name).on_error(close_db) ); - db->create_function( - "ikarus_default_value", - 1, - SQLITE_UTF8 | SQLITE_DETERMINISTIC, + IKARUS_TRYRV_OR_FAIL( nullptr, - [](sqlite3_context * ctx, int argc, sqlite3_value ** argv) { - if (sqlite3_value_type(argv[0]) != SQLITE_TEXT) { - sqlite3_result_error( - ctx, - "expected the 'schema' parameter to be of type text", - SQLITE_MISMATCH - ); - return; + "failed to create custom `ikarus_default_value` SQLite function: {}", + IkarusErrorInfo_Database_QueryFailed, + db->create_function( + "ikarus_default_value", + 1, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + nullptr, + [](sqlite3_context * ctx, int argc, sqlite3_value ** argv) { + if (sqlite3_value_type(argv[0]) != SQLITE_TEXT) { + sqlite3_result_error(ctx, "expected the 'schema' parameter to be of type text", SQLITE_MISMATCH); + return; + } + + auto const schema_json = reinterpret_cast(sqlite3_value_text(argv[0])); + + auto schema_res = IkarusValueSchema::from_json_str(schema_json); + + if (schema_res.is_error()) { + sqlite3_result_error(ctx, "failed to parse schema", SQLITE_ERROR); + return; + } + + auto schema = std::move(schema_res).unwrap_value(); + + auto default_value_json = IkarusValue::to_json(schema.default_value()).dump(); + + sqlite3_result_text(ctx, default_value_json.data(), default_value_json.size(), SQLITE_TRANSIENT); } - - auto const schema_json = - reinterpret_cast(sqlite3_value_text(argv[0])); - - auto schema_res = IkarusValueSchema::from_json_str(schema_json); - - if (schema_res.is_error()) { - sqlite3_result_error( - ctx, - "failed to parse schema", - SQLITE_ERROR - ); - return; - } - - auto schema = std::move(schema_res).unwrap_value(); - - auto default_value_json = - IkarusValue::to_json(schema.default_value()).dump(); - - sqlite3_result_text( - ctx, - default_value_json.data(), - default_value_json.size(), - SQLITE_TRANSIENT - ); - } + ) ); return std::move(db); } -IkarusProject * ikarus_project_create( - char const * path, - char const * name, - IkarusErrorData * error_out -) { +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( @@ -147,18 +125,10 @@ IkarusProject * ikarus_project_create( 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", + 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); } if (auto db = create_impl(fs_path.c_str(), name, error_out); !db) { @@ -167,18 +137,9 @@ IkarusProject * ikarus_project_create( boost::system::error_code ec; boost::filesystem::remove(fs_path, ec); - IKARUS_FAIL_IF( - ec, - nullptr, - "failed to remove project db", - IkarusErrorInfo_Filesystem_MissingPermissions - ); + 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 - ); + IKARUS_FAIL(nullptr, "failed to insert project name into metadata: {}", IkarusErrorInfo_Database_QueryFailed); return nullptr; } else { @@ -186,10 +147,7 @@ IkarusProject * ikarus_project_create( } } -IkarusProject * ikarus_project_create_in_memory( - char const * name, - IkarusErrorData * error_out -) { +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), @@ -212,18 +170,13 @@ IkarusProject * ikarus_project_create_in_memory( nullptr, "failed to insert project name into metadata: {}", IkarusErrorInfo_Database_QueryFailed, - db->execute( - "INSERT INTO `metadata`(`key`, `value`) VALUES(?, ?)", - DB_PROJECT_NAME_KEY, - name - ) + 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) { +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), @@ -252,29 +205,19 @@ ikarus_project_open(char const * path, IkarusErrorData * error_out) { nullptr, "failed to retrieve project name from metadata: {}", IkarusErrorInfo_Database_QueryFailed, - db->query_one( - "SELECT `value` FROM `metadata` WHERE `key` = ?", - DB_PROJECT_NAME_KEY - ) + db->query_one("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 -) { +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 -) { +void ikarus_project_set_name(IkarusProject * project, char const * new_name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, IKARUS_VOID_RETURN); IKARUS_FAIL_IF_NULL(new_name, IKARUS_VOID_RETURN); @@ -289,18 +232,11 @@ void ikarus_project_set_name( IKARUS_VOID_RETURN, "failed to update project name: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->execute( - "UPDATE `metadata` SET `value` = ? WHERE `key` = ?", - new_name, - DB_PROJECT_NAME_KEY - ) + 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 -) { +char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); return project->path.c_str(); @@ -323,18 +259,11 @@ void ikarus_project_get_entities( IKARUS_VOID_RETURN, "unable to fetch project entities from database: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered( - "SELECT `id` FROM `entities`", - ids_out, - ids_out_size - ) + project->db->query_many_buffered("SELECT `id` FROM `entities`", ids_out, ids_out_size) ); } -size_t ikarus_project_get_entity_count( - IkarusProject const * project, - IkarusErrorData * error_out -) { +size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, 0); IKARUS_VTRYRV_OR_FAIL( @@ -365,18 +294,11 @@ void ikarus_project_get_blueprints( , "unable to fetch project blueprints from database: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered( - "SELECT `id` FROM `blueprints`", - ids_out, - ids_out_size - ) + project->db->query_many_buffered("SELECT `id` FROM `blueprints`", ids_out, ids_out_size) ); } -size_t ikarus_project_get_blueprint_count( - IkarusProject const * project, - IkarusErrorData * error_out -) { +size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, 0); IKARUS_VTRYRV_OR_FAIL( From 90790b28ea521310ff37f72d866220c8e4a9f801 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 12:19:22 +0100 Subject: [PATCH 076/166] add modulemap --- include/module.modulemap | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 include/module.modulemap diff --git a/include/module.modulemap b/include/module.modulemap new file mode 100644 index 0000000..72846a4 --- /dev/null +++ b/include/module.modulemap @@ -0,0 +1,13 @@ +module libikarus { + header "ikarus/objects/blueprint.h" + header "ikarus/objects/entity.h" + header "ikarus/objects/property.h" + header "ikarus/persistence/project.h" + header "ikarus/values/data.h" + header "ikarus/values/schema.h" + header "ikarus/values/value.h" + header "ikarus/errors.h" + header "ikarus/macros.h" + header "ikarus/stdtypes.h" + export * +} \ No newline at end of file From 8ecb719b92ec958b48db87c7f25936341531ad1a Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 13:52:06 +0100 Subject: [PATCH 077/166] update cppbase --- vendor/cppbase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/cppbase b/vendor/cppbase index 82da677..f547f1c 160000 --- a/vendor/cppbase +++ b/vendor/cppbase @@ -1 +1 @@ -Subproject commit 82da6775fc56649cff6e611d6826a8e8fd321baa +Subproject commit f547f1cc0ac7bfa493d9c2c22c8222f02e67c1a5 From 6d881e776fd935f1c44dfc589a1648a8c7f59eed Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 14:26:55 +0100 Subject: [PATCH 078/166] fixup IKA_API os detection --- include/ikarus/macros.h | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/include/ikarus/macros.h b/include/ikarus/macros.h index e7714dc..a54809b 100644 --- a/include/ikarus/macros.h +++ b/include/ikarus/macros.h @@ -1,23 +1,28 @@ #pragma once -#define IKA_API +#if defined(_WIN32) + #define IKA_OS_FAMILY_WINDOWS + #define IKA_OS_WIN + #define IKA_API __declspec(dllexport) +#else + #define IKA_OS_FAMILY_UNIX + #define IKA_API __attribute__((visibility("default"))) -#if defined(__unix__) - #define IKA_OS_FAMILY_UNIX - #define IKA_API __attribute__((visibility("default"))) + #if defined(linux) + #define IKA_OS_LINUX + #elif defined(__APPLE__) + #define IKA_OS_MAC + #endif +#endif - #if defined(linux) - #define IKA_OS_LINUX - #endif -#elif defined(_WIN32) - #define IKA_OS_WIN - #define IKA_API __declspec(dllexport) +#ifndef IKA_API + #define IKA_API #endif #ifdef __cplusplus - #define IKARUS_BEGIN_HEADER extern "C" { - #define IKARUS_END_HEADER } + #define IKARUS_BEGIN_HEADER extern "C" { + #define IKARUS_END_HEADER } #else - #define IKARUS_BEGIN_HEADER - #define IKARUS_END_HEADER + #define IKARUS_BEGIN_HEADER + #define IKARUS_END_HEADER #endif From e03e1140a191993f047ed1c70a58d42dad037e83 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 14:30:16 +0100 Subject: [PATCH 079/166] hide symbols by default --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a007d4e..51c30ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,8 @@ set_target_properties( POSITION_INDEPENDENT_CODE TRUE ) +target_compile_options(libikarus PRIVATE "-fvisibility=hidden" "-fvisibility-inlines-hidden") + target_include_directories( libikarus PUBLIC From b37a60e32fa7a3bdfb0d71cc83963bbfa820d5c8 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 16:25:40 +0100 Subject: [PATCH 080/166] address crossplatform (linux) incompatibilities --- CMakeLists.txt | 2 +- src/ikarus/persistence/project.cpp | 4 +++- vendor/cppbase | 2 +- vendor/sqlitecpp | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51c30ed..6af3d4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.31) +cmake_minimum_required(VERSION 3.30) project(ikarus) option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF) diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 692e8f8..8dfb3ed 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -3,7 +3,9 @@ #include #include -#include +#include +#include +#include #include diff --git a/vendor/cppbase b/vendor/cppbase index f547f1c..5b3c517 160000 --- a/vendor/cppbase +++ b/vendor/cppbase @@ -1 +1 @@ -Subproject commit f547f1cc0ac7bfa493d9c2c22c8222f02e67c1a5 +Subproject commit 5b3c5172d2ba1c10b40df9bcf06a588da2f55cc7 diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 5fd5731..5eaa8c3 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 5fd5731c98d6e260e35d05a59c99822e32fe7191 +Subproject commit 5eaa8c3e76a21895bef4ed55bf288533dc31cf2a From 24335a0a1feef00028bc6dc7acce265f71c4e99e Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 18:42:39 +0100 Subject: [PATCH 081/166] add missing flags in project funcs --- src/ikarus/errors.cpp | 35 ++++++++----------------- src/ikarus/persistence/project.cpp | 42 +++++++++++++++--------------- 2 files changed, 32 insertions(+), 45 deletions(-) diff --git a/src/ikarus/errors.cpp b/src/ikarus/errors.cpp index 07f7a15..b8dcc7d 100644 --- a/src/ikarus/errors.cpp +++ b/src/ikarus/errors.cpp @@ -4,11 +4,7 @@ #include -void safe_strcpy( - std::string_view const src, - char * dest, - std::size_t const dest_size -) { +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') { @@ -21,7 +17,7 @@ void safe_strcpy( dest[i] = '\0'; } -char const * ikarus_get_error_info_name(IkarusErrorInfo info) { +char const * ikarus_error_info_get_name(IkarusErrorInfo info) { switch (info) { case IkarusErrorInfo_None: return "None"; @@ -29,34 +25,25 @@ char const * ikarus_get_error_info_name(IkarusErrorInfo info) { case IkarusErrorInfo_Client_InvalidInput: return "Client::InvalidInput"; case IkarusErrorInfo_Client_NonExistent: return "Client::NonExistent"; case IkarusErrorInfo_Client_InvalidFormat: return "Client::InvalidFormat"; - case IkarusErrorInfo_Client_ConstraintViolated: - return "Client::ConstraintViolated"; + case IkarusErrorInfo_Client_ConstraintViolated: return "Client::ConstraintViolated"; case IkarusErrorInfo_Filesystem_NotFound: return "Filesystem::NotFound"; - case IkarusErrorInfo_Filesystem_AlreadyExists: - return "Filesystem::AlreadyExists"; - case IkarusErrorInfo_Filesystem_MissingPermissions: - return "Filesystem::MissingPermissions"; - case IkarusErrorInfo_Filesystem_InsufficientSpace: - return "Filesystem::InsufficientSpace"; - case IkarusErrorInfo_Filesystem_InvalidPath: - return "Filesystem::InvalidPath"; + case IkarusErrorInfo_Filesystem_AlreadyExists: return "Filesystem::AlreadyExists"; + case IkarusErrorInfo_Filesystem_MissingPermissions: 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: return "Database::ConnectionFailed"; case IkarusErrorInfo_Database_QueryFailed: return "Database::QueryFailed"; - case IkarusErrorInfo_Database_MigrationFailed: - return "Database::MigrationFailed"; + case IkarusErrorInfo_Database_MigrationFailed: return "Database::MigrationFailed"; case IkarusErrorInfo_Database_InvalidState: return "Database::InvalidState"; case IkarusErrorInfo_OS_SystemCallFailed: return "OS::SystemCallFailed"; case IkarusErrorInfo_OS_InvalidReturnValue: return "OS::InvalidReturnValue"; case IkarusErrorInfo_OS_InsufficientMemory: return "OS::InsufficientMemory"; - case IkarusErrorInfo_LibIkarus_InvalidState: - return "LibIkarus::InvalidState"; - case IkarusErrorInfo_LibIkarus_CannotPerformOperation: - return "LibIkarus::CannotPerformOperation"; + case IkarusErrorInfo_LibIkarus_InvalidState: return "LibIkarus::InvalidState"; + case IkarusErrorInfo_LibIkarus_CannotPerformOperation: return "LibIkarus::CannotPerformOperation"; case IkarusErrorInfo_LibIkarus_Timeout: return "LibIkarus::Timeout"; default: return "Invalid"; diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 8dfb3ed..2501eed 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -149,7 +149,11 @@ IkarusProject * ikarus_project_create(char const * path, char const * name, Ikar } } -IkarusProject * ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out) { +IkarusProject * ikarus_project_create_in_memory( + char const * name, + IkarusProjectCreateInMemoryFlags flags, + IkarusErrorData * error_out +) { IKARUS_FAIL_IF_NULL(name, nullptr); IKARUS_FAIL_IF( cppbase::is_empty_or_blank(name), @@ -178,7 +182,7 @@ IkarusProject * ikarus_project_create_in_memory(char const * name, IkarusErrorDa } } -IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_out) { +IkarusProject * ikarus_project_open(char const * path, IkarusProjectOpenFlags flags, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(path, nullptr); IKARUS_FAIL_IF( cppbase::is_empty_or_blank(path), @@ -213,31 +217,27 @@ IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_o return new IkarusProject{name, path, std::move(db)}; } +void ikarus_project_close(struct IkarusProject * project, IkarusProjectCloseFlags flags, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, IKARUS_VOID_RETURN); + + if (project->db) { + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to close project db: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->close() + ); + } + + delete project; +} + 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_VOID_RETURN); - IKARUS_FAIL_IF_NULL(new_name, IKARUS_VOID_RETURN); - - IKARUS_FAIL_IF( - cppbase::is_empty_or_blank(new_name), - IKARUS_VOID_RETURN, - "name must not be empty", - IkarusErrorInfo_Client_InvalidInput - ); - - IKARUS_TRYRV_OR_FAIL( - IKARUS_VOID_RETURN, - "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); From 9ea02d5cb1aaaed8b874c75f2cc834a43fda0852 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 18:48:24 +0100 Subject: [PATCH 082/166] improve C header compatibility --- include/ikarus/errors.h | 5 +- include/ikarus/objects/blueprint.h | 28 +++--- include/ikarus/objects/entity.h | 131 ++++++++++++--------------- include/ikarus/objects/property.h | 59 +++++++----- include/ikarus/persistence/project.h | 59 +++++------- include/ikarus/stdtypes.h | 10 +- include/ikarus/values/data.h | 8 +- include/ikarus/values/schema.h | 8 +- include/ikarus/values/value.h | 8 +- 9 files changed, 151 insertions(+), 165 deletions(-) diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index eb123b0..efd2155 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -4,6 +4,7 @@ /// \author Folling #include +#include /// \addtogroup errors Errors /// \brief Error handling within libikarus @@ -116,11 +117,11 @@ IKA_API char const * ikarus_error_info_get_name(enum IkarusErrorInfo info); /// \brief Checks if an error data is a success. /// \param data The error data to check. /// \return True if the error data is a success, false otherwise. -IKA_API bool ikarus_error_data_is_success(IkarusErrorData const * data); +IKA_API bool ikarus_error_data_is_success(struct IkarusErrorData const * data); /// \brief Checks if an error data is an error. /// \param data The error data to check. /// \return True if the error data is an error, false otherwise. -IKA_API bool ikarus_error_data_is_error(IkarusErrorData const * data); +IKA_API bool ikarus_error_data_is_error(struct IkarusErrorData const * data); IKARUS_END_HEADER diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 0631126..bd4bb1c 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -3,13 +3,17 @@ /// \file blueprint.h /// \author Folling +/// \defgroup blueprints Blueprints +/// \brief Blueprints are templates for entities. +/// @{ + #include #include #include -/// \defgroup blueprints Blueprints -/// \brief Blueprints are templates for entities. -/// @{ +struct IkarusProject; +struct IkarusEntity; +struct IkarusProperty; IKARUS_BEGIN_HEADER @@ -25,7 +29,7 @@ struct IkarusBlueprint; /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return True if the blueprint exists, false otherwise or if an error occurs. -IKA_API bool ikarus_blueprint_exists(IkarusBlueprint * blueprint, IkarusErrorData * error_out); +IKA_API bool ikarus_blueprint_exists(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); /// \brief Flags for creating a blueprint. enum IkarusBlueprintCreateFlags { @@ -44,10 +48,10 @@ enum IkarusBlueprintCreateFlags { /// \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 struct IkarusBlueprint * ikarus_blueprint_create( struct IkarusProject * project, char const * name, - IkarusBlueprintCreateFlags flags, + enum IkarusBlueprintCreateFlags flags, struct IkarusErrorData * error_out ); @@ -68,10 +72,10 @@ enum IkarusBlueprintCreateFromEntityFlags { /// \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( +IKA_API struct IkarusBlueprint * ikarus_blueprint_create_from_entity( struct IkarusEntity * entity, char const * name, - IkarusBlueprintCreateFromEntityFlags flags, + enum IkarusBlueprintCreateFromEntityFlags flags, struct IkarusErrorData * error_out ); @@ -90,7 +94,7 @@ enum IkarusBlueprintCopyFlags { /// \return The copied blueprint or null if an error occurs. IKA_API struct IkarusBlueprint * ikarus_blueprint_copy( struct IkarusBlueprint * blueprint, - IkarusBlueprintCopyFlags flags, + enum IkarusBlueprintCopyFlags flags, struct IkarusErrorData * error_out ); @@ -109,8 +113,8 @@ enum IkarusBlueprintDeleteFlags { /// \param error_out \see errors.h IKA_API void ikarus_blueprint_delete( struct IkarusBlueprint * blueprint, - IkarusBlueprintDeleteFlags flags, - IkarusErrorData * error_out + enum IkarusBlueprintDeleteFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets the project a blueprint belongs to. @@ -150,7 +154,7 @@ enum IkarusBlueprintSetNameFlags { IKA_API void ikarus_blueprint_set_name( struct IkarusBlueprint * blueprint, char const * name, - IkarusBlueprintSetNameFlags flags, + enum IkarusBlueprintSetNameFlags flags, struct IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 990be61..7b9518f 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,15 +1,19 @@ #pragma once -#include -#include -#include - /// \file entity.h /// \author Folling /// \defgroup entities Entities /// \brief Entities are the core building blocks of Ikarus. +#include +#include +#include + +struct IkarusProject; +struct IkarusEntity; +struct IkarusProperty; + IKARUS_BEGIN_HEADER /// \brief Entities are the core building blocks of Ikarus. @@ -38,8 +42,7 @@ enum IkarusEntityCreateFlags { /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return True if the entity exists, false otherwise or if an error occurs. -IKA_API bool -ikarus_entity_exists(IkarusEntity * entity, IkarusErrorData * error_out); +IKA_API bool ikarus_entity_exists(struct IkarusEntity * entity, struct IkarusErrorData * error_out); /// \brief Creates a new entity. /// \param project The project to create the entity in. @@ -51,11 +54,11 @@ ikarus_entity_exists(IkarusEntity * entity, IkarusErrorData * error_out); /// \param flags Flags for creating the entity. /// \param error_out \see errors.h /// \return The created entity or null if an error occurs. -IKA_API IkarusEntity * ikarus_entity_create( +IKA_API struct IkarusEntity * ikarus_entity_create( struct IkarusProject * project, char const * name, - IkarusEntityCreateFlags flags, - IkarusErrorData * error_out + enum IkarusEntityCreateFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for deleting an entity. @@ -71,9 +74,9 @@ enum IkarusEntityDeleteFlags { /// \param flags Flags for deleting the entity. /// \param error_out \see errors.h IKA_API void ikarus_entity_delete( - IkarusEntity * entity, - IkarusEntityDeleteFlags flags, - IkarusErrorData * error_out + struct IkarusEntity * entity, + enum IkarusEntityDeleteFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for copying an entity. @@ -89,11 +92,8 @@ enum IkarusEntityCopyFlags { /// \param flags Flags for copying the entity. /// \param error_out \see errors.h /// \return The copied entity or null if an error occurs. -IKA_API IkarusEntity * ikarus_entity_copy( - IkarusEntity * entity, - IkarusEntityCopyFlags flags, - IkarusErrorData * error_out -); +IKA_API struct IkarusEntity * +ikarus_entity_copy(struct IkarusEntity * entity, enum IkarusEntityCopyFlags flags, struct IkarusErrorData * error_out); /// \brief Gets the project an entity belongs to. /// \param entity The entity to get the project of. @@ -102,7 +102,7 @@ IKA_API IkarusEntity * ikarus_entity_copy( /// \param error_out \see errors.h /// \return The project the entity belongs to. IKA_API struct IkarusProject * -ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out); +ikarus_entity_get_project(struct IkarusEntity * entity, struct IkarusErrorData * error_out); /// \brief Gets the name of an entity. /// \param entity The entity to get the name of. @@ -110,8 +110,7 @@ ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out); /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The name of the entity. -IKA_API char const * -ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out); +IKA_API char const * ikarus_entity_get_name(struct IkarusEntity * entity, struct IkarusErrorData * error_out); /// \brief Flags for setting the name of an entity. enum IkarusEntitySetNameFlags { @@ -129,10 +128,10 @@ enum IkarusEntitySetNameFlags { /// \param flags Flags for setting the name of the entity. /// \param error_out \see errors.h IKA_API void ikarus_entity_set_name( - IkarusEntity * entity, + struct IkarusEntity * entity, char const * name, - IkarusEntitySetNameFlags flags, - IkarusErrorData * error_out + enum IkarusEntitySetNameFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets whether an entity is linked to a blueprint. @@ -146,9 +145,9 @@ IKA_API void ikarus_entity_set_name( /// \param error_out \see errors.h /// \return True if the entity is linked to the blueprint, false otherwise or if an error occurs. IKA_API bool ikarus_entity_is_linked_to_blueprint( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusBlueprint * blueprint, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Gets the blueprints an entity is linked to. @@ -159,9 +158,9 @@ IKA_API bool ikarus_entity_is_linked_to_blueprint( /// or undefined if an error occurs. /// \param error_out \see errors.h IKA_API struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( - IkarusEntity * entity, + struct IkarusEntity * entity, size_t * size_out, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Gets the number of blueprints an entity is linked to. @@ -170,10 +169,8 @@ IKA_API struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of linked blueprints or 0 if an error occurs. -IKA_API size_t ikarus_entity_get_linked_blueprints_count( - IkarusEntity * entity, - IkarusErrorData * error_out -); +IKA_API size_t +ikarus_entity_get_linked_blueprints_count(struct IkarusEntity * entity, struct IkarusErrorData * error_out); /// \brief Flags for linking an entity to a blueprint. enum IkarusEntityLinkBlueprintFlags { @@ -195,10 +192,10 @@ enum IkarusEntityLinkBlueprintFlags { /// \param flags Flags for linking the entity to the blueprint. /// \param error_out \see errors.h IKA_API void ikarus_entity_link_blueprint( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusBlueprint * blueprint, - IkarusEntityLinkBlueprintFlags flags, - IkarusErrorData * error_out + enum IkarusEntityLinkBlueprintFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for unlinking an entity from a blueprint. @@ -220,10 +217,10 @@ enum IkarusEntityUnlinkBlueprintFlags { /// \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 IkarusEntity * entity, struct IkarusBlueprint * blueprint, - IkarusEntityUnlinkBlueprintFlags flags, - IkarusErrorData * error_out + enum IkarusEntityUnlinkBlueprintFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets whether an entity has a value. @@ -234,11 +231,8 @@ IKA_API void ikarus_entity_unlink_blueprint( /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return True if the entity has a value with the name, false otherwise or if an error occurs. -IKA_API bool ikarus_entity_has_value( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out -); +IKA_API bool +ikarus_entity_has_value(struct IkarusEntity * entity, char const * name, struct IkarusErrorData * error_out); /// \brief Struct for an entity value. struct IkarusEntityValue { @@ -256,11 +250,8 @@ struct IkarusEntityValue { /// or undefined if an error occurs. /// \param error_out \see errors.h /// \return The values or null if an error occurs. -IKA_API IkarusEntityValue * ikarus_entity_get_values( - IkarusEntity * entity, - size_t * size_out, - IkarusErrorData * error_out -); +IKA_API struct IkarusEntityValue * +ikarus_entity_get_values(struct IkarusEntity * entity, size_t * size_out, struct IkarusErrorData * error_out); /// \brief Gets a value of an entity. /// \param entity The entity to get the value of. @@ -271,11 +262,8 @@ IKA_API IkarusEntityValue * ikarus_entity_get_values( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The value, in json format of or null if an error occurs. \see value.h -IKA_API char const * ikarus_entity_get_value( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out -); +IKA_API char const * +ikarus_entity_get_value(struct IkarusEntity * entity, char const * name, struct IkarusErrorData * error_out); /// \brief Flags for setting a value of an entity. enum IkarusEntitySetValueFlags { @@ -297,11 +285,11 @@ enum IkarusEntitySetValueFlags { /// \param flags Flags for setting the value. /// \param error_out \see errors.h IKA_API void ikarus_entity_set_value( - IkarusEntity * entity, + struct IkarusEntity * entity, char const * name, char const * value, - IkarusEntitySetValueFlags flags, - IkarusErrorData * error_out + enum IkarusEntitySetValueFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for deleting a value of an entity. @@ -320,10 +308,10 @@ enum IkarusEntityDeleteValueFlags { /// \param flags Flags for deleting the value. /// \param error_out \see errors.h IKA_API void ikarus_entity_delete_value( - IkarusEntity * entity, + struct IkarusEntity * entity, char const * name, - IkarusEntityDeleteValueFlags flags, - IkarusErrorData * error_out + enum IkarusEntityDeleteValueFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets whether an entity has a property value. @@ -337,9 +325,9 @@ IKA_API void ikarus_entity_delete_value( /// \param error_out \see errors.h /// \return True if the entity has a value for the property, false otherwise or if an error occurs. IKA_API bool ikarus_entity_has_property_value( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusProperty * property, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Struct for an entity property value. @@ -358,11 +346,8 @@ struct IkarusEntityPropertyValue { /// returned array or undefined if an error occurs. /// \param error_out \see errors.h /// \return The property values, or null if an error occurs. -IKA_API IkarusEntityPropertyValue * ikarus_entity_get_property_values( - IkarusEntity * entity, - size_t * size_out, - IkarusErrorData * error_out -); +IKA_API struct IkarusEntityPropertyValue * +ikarus_entity_get_property_values(struct IkarusEntity * entity, size_t * size_out, struct IkarusErrorData * error_out); /// \brief Gets the value of a property of an entity. /// \param entity The entity to get the value of. @@ -376,9 +361,9 @@ IKA_API IkarusEntityPropertyValue * ikarus_entity_get_property_values( /// \return The value, in json format of or null if an error occurs. \see /// value.h IKA_API char const * ikarus_entity_get_property_value( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusProperty * property, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Flags for setting the value of a property of an entity. @@ -403,11 +388,11 @@ enum IkarusEntitySetPropertyValueFlags { /// \param flags Flags for setting the property value. /// \param error_out \see errors.h IKA_API void ikarus_entity_set_property_value( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusProperty * property, char const * value, - IkarusEntitySetPropertyValueFlags flags, - IkarusErrorData * error_out + enum IkarusEntitySetPropertyValueFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for clearing the value of a property of an entity. @@ -427,10 +412,10 @@ enum IkarusEntityClearPropertyValueFlags { /// \param flags Flags for clearing the property value. /// \param error_out \see errors.h IKA_API void ikarus_entity_clear_property_value( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusProperty * property, - IkarusEntitySetPropertyValueFlags flags, - IkarusErrorData * error_out + enum IkarusEntitySetPropertyValueFlags flags, + struct IkarusErrorData * error_out ); IKARUS_END_HEADER diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index 0a6c3d1..a24d3c3 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -3,13 +3,16 @@ /// \file property.h /// \author Folling +/// \defgroup properties Properties +/// \brief Properties define the structure and types of data. +/// @{ + #include #include #include -/// \defgroup properties Properties -/// \brief Properties define the structure and types of data. -/// @{ +struct IkarusProject; +struct IkarusBlueprint; IKARUS_BEGIN_HEADER @@ -41,7 +44,7 @@ struct IkarusProperty; /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return True if the property exists, false otherwise or if an error occurs. -IKA_API bool ikarus_property_exists(IkarusProperty * property, IkarusErrorData * error_out); +IKA_API bool ikarus_property_exists(struct IkarusProperty * property, struct IkarusErrorData * error_out); /// \brief Flags for creating a property. enum IkarusPropertyCreateFlags { @@ -69,13 +72,13 @@ enum IkarusPropertyCreateFlags { /// \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( +IKA_API struct IkarusProperty * ikarus_property_create( struct IkarusBlueprint * blueprint, char const * name, char const * schema, char const * default_value, - IkarusPropertyCreateFlags flags, - IkarusErrorData * error_out + enum IkarusPropertyCreateFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for copying a property. @@ -91,8 +94,11 @@ enum IkarusPropertyCopyFlags { /// \param flags Flags for copying the property. /// \param error_out \see errors.h /// \return The copied property or NULL if an error occurred. -IKA_API IkarusProperty * -ikarus_property_copy(IkarusProperty * property, IkarusPropertyCopyFlags flags, IkarusErrorData * error_out); +IKA_API struct IkarusProperty * ikarus_property_copy( + struct IkarusProperty * property, + enum IkarusPropertyCopyFlags flags, + struct IkarusErrorData * error_out +); /// \brief Flags for deleting a property. enum IkarusPropertyDeleteFlags { @@ -104,8 +110,11 @@ enum IkarusPropertyDeleteFlags { /// \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); +IKA_API void ikarus_property_delete( + struct IkarusProperty * property, + enum IkarusPropertyDeleteFlags flags, + struct IkarusErrorData * error_out +); /// \brief Get the project a property belongs to. /// \param property The property to get the project of. @@ -114,7 +123,8 @@ ikarus_property_delete(IkarusProperty * property, IkarusPropertyDeleteFlags flag /// \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); +IKA_API struct IkarusProject * +ikarus_property_get_project(struct IkarusProperty * property, struct IkarusErrorData * error_out); /// \brief Get the name of a property. /// \param property The property to get the name of. @@ -123,7 +133,7 @@ IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty * prop /// \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); +IKA_API char const * ikarus_property_get_name(struct IkarusProperty * property, struct IkarusErrorData * error_out); /// \brief Flags for setting the name of a property. enum IkarusPropertySetNameFlags { @@ -142,10 +152,10 @@ enum IkarusPropertySetNameFlags { /// \param error_out \see errors.h /// \remark Ownership remains with the caller. IKA_API void ikarus_property_set_name( - IkarusProperty * property, + struct IkarusProperty * property, char const * name, - IkarusPropertySetNameFlags flags, - IkarusErrorData * error_out + enum IkarusPropertySetNameFlags flags, + struct IkarusErrorData * error_out ); /// \brief Get the schema of a property. @@ -154,7 +164,7 @@ IKA_API void ikarus_property_set_name( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The schema of the property in JSON format or null if an error occurred. \see schema.h -IKA_API char const * ikarus_property_get_schema(IkarusProperty * property, IkarusErrorData * error_out); +IKA_API char const * ikarus_property_get_schema(struct IkarusProperty * property, struct IkarusErrorData * error_out); /// \brief Flags for setting the schema of a property. enum IkarusPropertySetSchemaFlags { @@ -176,11 +186,11 @@ enum IkarusPropertySetSchemaFlags { /// \param flags Flags for setting the schema of the property. /// \param error_out \see errors.h IKA_API void ikarus_property_set_schema( - IkarusProperty * property, + struct IkarusProperty * property, char const * schema, char const * new_default_value, - IkarusPropertySetSchemaFlags flags, - IkarusErrorData * error_out + enum IkarusPropertySetSchemaFlags flags, + struct IkarusErrorData * error_out ); /// \brief Get the default value of a property. @@ -189,7 +199,8 @@ IKA_API void ikarus_property_set_schema( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The default value data of the property in JSON format or null if an error occurred. \see data.h -IKA_API char const * ikarus_property_get_default_value(IkarusProperty * property, IkarusErrorData * error_out); +IKA_API char const * +ikarus_property_get_default_value(struct IkarusProperty * property, struct IkarusErrorData * error_out); /// \details The default value must match the schema of the property. @@ -210,10 +221,10 @@ enum IkarusPropertySetDefaultValueFlags { /// \param flags Flags for setting the default value of the property. /// \param error_out \see errors.h IKA_API void ikarus_property_set_default_value( - IkarusProperty * property, + struct IkarusProperty * property, char const * value, - IkarusPropertySetDefaultValueFlags flags, - IkarusErrorData * error_out + enum IkarusPropertySetDefaultValueFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets all values for a property. diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index af40147..f5da0ad 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -3,17 +3,15 @@ /// \file project.h /// \author Folling -#include - -#include -#include -#include - /// \defgroup projects Projects /// \brief Projects are the persistence mechanism of Ikarus. Each project contains a set of objects. /// \details Projects are stored on the filesystem. Each project has a name. /// @{ +#include +#include +#include + IKARUS_BEGIN_HEADER /// \brief An Ikarus project. @@ -44,8 +42,8 @@ enum IkarusProjectCreateFlags { IKA_API struct IkarusProject * ikarus_project_create( char const * path, char const * name, - IkarusProjectCreateFlags flags, - IkarusErrorData * error_out + enum IkarusProjectCreateFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for creating a project in memory. @@ -64,8 +62,8 @@ enum IkarusProjectCreateInMemoryFlags { /// \remark Must be closed with #ikarus_project_close. IKA_API struct IkarusProject * ikarus_project_create_in_memory( char const * name, - IkarusProjectCreateInMemoryFlags flags, - IkarusErrorData * error_out + enum IkarusProjectCreateInMemoryFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for opening a project. @@ -82,11 +80,8 @@ enum IkarusProjectOpenFlags { /// \param error_out \see errors.h /// \return The opened project or null if an error occurs. /// \remark Must be closed with #ikarus_project_close. -IKA_API struct IkarusProject * ikarus_project_open( - char const * path, - IkarusProjectOpenFlags flags, - IkarusErrorData * error_out -); +IKA_API struct IkarusProject * +ikarus_project_open(char const * path, enum IkarusProjectOpenFlags flags, struct IkarusErrorData * error_out); /// \brief Flags for closing a project in memory. enum IkarusProjectCloseFlags { @@ -105,8 +100,8 @@ enum IkarusProjectCloseFlags { /// \remark Mutually exclusive with #ikarus_project_delete. IKA_API void ikarus_project_close( struct IkarusProject * project, - IkarusProjectCloseFlags flags, - IkarusErrorData * error_out + enum IkarusProjectCloseFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for deleting a project. @@ -125,8 +120,8 @@ enum IkarusProjectDeleteFlags { /// \remark Mutually exclusive with #ikarus_project_close. IKA_API void ikarus_project_delete( struct IkarusProject * project, - IkarusProjectDeleteFlags flags, - IkarusErrorData * error_out + enum IkarusProjectDeleteFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets the name of a project. @@ -135,10 +130,7 @@ IKA_API void ikarus_project_delete( /// \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 -); +IKA_API char const * ikarus_project_get_name(struct IkarusProject const * project, struct IkarusErrorData * error_out); /// \brief Gets the path of a project. /// \param project The project to delete. @@ -146,10 +138,7 @@ IKA_API char const * ikarus_project_get_name( /// \pre \li Must exist. /// \param error_out \see errors.h /// \returns The path of the project or null if an error occurs. -IKA_API char const * ikarus_project_get_path( - struct IkarusProject const * project, - IkarusErrorData * error_out -); +IKA_API char const * ikarus_project_get_path(struct IkarusProject const * project, struct IkarusErrorData * error_out); /// \brief Gets all entities in a project. /// \param project The project from which to get the entities. @@ -162,7 +151,7 @@ IKA_API char const * ikarus_project_get_path( IKA_API struct IkarusEntity ** ikarus_project_get_entities( struct IkarusProject const * project, size_t * size_out, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Gets how many entities in a project there are. @@ -171,10 +160,8 @@ IKA_API struct IkarusEntity ** ikarus_project_get_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The count or 0 if an error occurs. -IKA_API size_t ikarus_project_get_entities_count( - IkarusProject const * project, - IkarusErrorData * error_out -); +IKA_API size_t +ikarus_project_get_entities_count(struct IkarusProject const * project, struct IkarusErrorData * error_out); /// \brief Gets all blueprints from a project. /// \param project The project from which to get the blueprints. @@ -187,7 +174,7 @@ IKA_API size_t ikarus_project_get_entities_count( IKA_API struct IkarusBlueprint ** ikarus_project_get_blueprints( struct IkarusProject const * project, size_t * size_out, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Gets how many blueprints in a project there are. @@ -196,10 +183,8 @@ IKA_API struct IkarusBlueprint ** ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The count or 0 if an error occurs. -IKA_API size_t ikarus_project_get_blueprints_count( - IkarusProject const * project, - IkarusErrorData * error_out -); +IKA_API size_t +ikarus_project_get_blueprints_count(struct IkarusProject const * project, struct IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h index 71d07c2..3f412eb 100644 --- a/include/ikarus/stdtypes.h +++ b/include/ikarus/stdtypes.h @@ -1,11 +1,11 @@ #pragma once #ifdef __cplusplus - #include - #include + #include + #include using std::size_t; #else - #include - #include - #include + #include + #include + #include #endif diff --git a/include/ikarus/values/data.h b/include/ikarus/values/data.h index d4e2367..694e640 100644 --- a/include/ikarus/values/data.h +++ b/include/ikarus/values/data.h @@ -1,14 +1,14 @@ #pragma once -#include -#include -#include - /// \file data.h /// \author Folling /// \addtogroup values Values +#include +#include +#include + IKARUS_BEGIN_HEADER /// \brief Data stores the actual information of a value. diff --git a/include/ikarus/values/schema.h b/include/ikarus/values/schema.h index 9e25e01..73997a6 100644 --- a/include/ikarus/values/schema.h +++ b/include/ikarus/values/schema.h @@ -1,14 +1,14 @@ #pragma once -#include -#include -#include - /// \file schema.h /// \author Folling /// \addtogroup values Values +#include +#include +#include + IKARUS_BEGIN_HEADER /// \brief Schemas define the type of value. diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index 870f551..076b8ad 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -1,15 +1,15 @@ #pragma once -#include -#include -#include - /// \file value.h /// \author Folling /// \defgroup entities Entities /// \brief Entities are the core building blocks of Ikarus. +#include +#include +#include + IKARUS_BEGIN_HEADER /// \brief Values are data containers for entities. From f85171a054e54dff6de9dce45aa7c51a51940aaf Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 13 Apr 2025 16:47:23 +0200 Subject: [PATCH 083/166] remove toolchains & cmake preset --- CMakePresets.json | 35 -------------------------------- tools/cmake/toolchains/mac.cmake | 5 ----- 2 files changed, 40 deletions(-) delete mode 100644 CMakePresets.json delete mode 100644 tools/cmake/toolchains/mac.cmake diff --git a/CMakePresets.json b/CMakePresets.json deleted file mode 100644 index 0772ba0..0000000 --- a/CMakePresets.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "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" - } - ] -} diff --git a/tools/cmake/toolchains/mac.cmake b/tools/cmake/toolchains/mac.cmake deleted file mode 100644 index b7120cd..0000000 --- a/tools/cmake/toolchains/mac.cmake +++ /dev/null @@ -1,5 +0,0 @@ -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") From 8eb20673185cd73054962962990a2cd25b2a26c1 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 22 Aug 2023 00:27:22 +0200 Subject: [PATCH 084/166] a new beginning Signed-off-by: Folling --- .clang-format | 222 ++++++++++++++++++ .gitignore | 58 +++++ .gitmodules | 9 + CMakeLists.txt | 55 +++++ README.md | 11 + docs/DoxyFile | 16 ++ docs/enum_format_fix.js | 11 + docs/header.html | 97 ++++++++ implementation_details | 12 + include/CMakeLists.txt | 8 + include/ikarus/macros.h | 27 +++ include/ikarus/stdtypes.h | 8 + include/ikarus/types/id.h | 58 +++++ include/ikarus/types/object.h | 164 +++++++++++++ include/ikarus/types/object_scope.h | 149 ++++++++++++ include/ikarus/types/object_type.h | 63 +++++ include/ikarus/types/property_type.h | 31 +++ include/ikarus/types/value.h | 161 +++++++++++++ src/CMakeLists.txt | 7 + src/types/object_scope.cpp | 209 +++++++++++++++++ src/types/value.cpp | 337 +++++++++++++++++++++++++++ vendor/CMakeLists.txt | 2 + vendor/catch2 | 1 + vendor/doxygen-awesome-css | 1 + vendor/sqlitecpp | 1 + 25 files changed, 1718 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 docs/DoxyFile create mode 100644 docs/enum_format_fix.js create mode 100644 docs/header.html create mode 100644 implementation_details create mode 100644 include/CMakeLists.txt create mode 100644 include/ikarus/macros.h create mode 100644 include/ikarus/stdtypes.h create mode 100644 include/ikarus/types/id.h create mode 100644 include/ikarus/types/object.h create mode 100644 include/ikarus/types/object_scope.h create mode 100644 include/ikarus/types/object_type.h create mode 100644 include/ikarus/types/property_type.h create mode 100644 include/ikarus/types/value.h create mode 100644 src/CMakeLists.txt create mode 100644 src/types/object_scope.cpp create mode 100644 src/types/value.cpp create mode 100644 vendor/CMakeLists.txt create mode 160000 vendor/catch2 create mode 160000 vendor/doxygen-awesome-css create mode 160000 vendor/sqlitecpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..0372fa7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,222 @@ +BasedOnStyle: Google + +AccessModifierOffset: -4 + +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: + Enabled: false +AlignConsecutiveBitFields: + Enabled: false +AlignConsecutiveDeclarations: + Enabled: false +AlignConsecutiveMacros: AcrossEmptyLines +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true + +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true + +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes + +BinPackArguments: false +BinPackParameters: false + +BitFieldColonSpacing: Both + +BraceWrapping: + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: MultiLine + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyNamespace: false + SplitEmptyRecord: false + +BreakAfterAttributes: Never +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeConceptDeclarations: Always +# BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: false + +ColumnLimit: 128 + +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 + +Cpp11BracedListStyle: true + +DerivePointerAlignment: false + +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: Always + +FixNamespaceComments: false + +IncludeBlocks: Regroup +IncludeCategories: + # Relative Includes + # "blubb.h" / "blubb/blubber.h" + - Regex: '^".+\.(h|hpp)"$' + Priority: 1 + + # C Includes + # + - Regex: '^<[a-z0-9_]+\.h>$' + Priority: 2 + + # C++ Includes + # + - Regex: '^<[a-z0-9_]+>$' + Priority: 3 + + # expected + # + - Regex: '^$' + Priority: 4 + + # libfmt + # + - Regex: '^$' + Priority: 5 + + # nlohmann::json + # + - Regex: '^$' + Priority: 6 + + # ranges + # + - Regex: '^$' + Priority: 7 + + # ICU + # + - Regex: '^$' + Priority: 8 + + # ranges + # + - Regex: '^$' + Priority: 9 + + # ranges + # + - Regex: '^$' + Priority: 10 + + # ranges + # + - Regex: '^$' + Priority: 11 + + # ranges + # + - Regex: '^$' + Priority: 12 + + # ranges + # ranges + # + - Regex: '^$' + Priority: 13 + +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: NoIndent +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: true +InsertNewlineAtEOF: true + +# InsertNewlineAtEOF: true +# IntegerLiteralSeparator: +# Binary: 0 +# Decimal: 3 +# Hex: -1 + +KeepEmptyLinesAtTheStartOfBlocks: false + +LambdaBodyIndentation: Signature +Language: Cpp + +# LineEnding: LF + +MaxEmptyLinesToKeep: 1 + +NamespaceIndentation: None + +PackConstructorInitializers: Never + +PointerAlignment: Middle +QualifierAlignment: Right +# QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ] +ReferenceAlignment: Left + +ReflowComments: true +# RemoveSemicolon: true + +RequiresClausePosition: OwnLine +# RequiresExpressionIndentation: OuterScope + +SeparateDefinitionBlocks: Always + +SortIncludes: CaseInsensitive +SortUsingDeclarations: true + +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false + +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: 1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++20 + +TabWidth: 4 +UseTab: Never diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7ab8cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# C++ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +build +docs/generated +gen/script_old/target +include/ikarus/entities +include/ikarus/project.h +src/generated/**/*.cpp +src/generated/**/*.hpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a26cc62 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "vendor/catch2"] + path = vendor/catch2 + url = git@github.com:catchorg/Catch2.git +[submodule "vendor/sqlitecpp"] + path = vendor/sqlitecpp + url = ssh://git@git.rewritesarebliss.com:16658/Folling/sqlitecpp.git +[submodule "vendor/doxygen-awesome-css"] + path = vendor/doxygen-awesome-css + url = git@github.com:jothepro/doxygen-awesome-css.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a7faf39 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.18) +project(ikarus) + +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_STANDARD 20) + +set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) + +add_subdirectory(vendor) +add_subdirectory(include) +add_subdirectory(src) + +add_library( + libikarus OBJECT + ${INCLUDE_FILES} + ${SOURCE_FILES} +) + +target_include_directories( + libikarus PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include +) + +target_include_directories( + libikarus PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/src +) + +target_link_libraries( + libikarus PUBLIC + Catch2::Catch2WithMain +) + +target_link_libraries( + libikarus PRIVATE + cppbase + sqlitecpp +) + +set_target_properties( + libikarus PROPERTIES + LINKER_LANGUAGE CXX +) + +add_executable(ikarus_tests ${SOURCE_FILES}) +target_link_libraries(ikarus_tests PRIVATE Catch2::Catch2WithMain) + +target_include_directories( + ikarus_tests PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include +) + +include(CTest) +include(vendor/catch2/extras/Catch.cmake) +catch_discover_tests(ikarus_tests) diff --git a/README.md b/README.md new file mode 100644 index 0000000..57b6325 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +### Data Longevity + +All data returned by libikarus is ephemeral and only represents the state of the project at the time of the request. +A snapshot if you will. +One must not rely on it representing the actual state of the project at any given time. The data is simply copied +from the underlying data sources and returned to the caller. + +No mechanisms are provided to avoid race conditions. LibIkarus itself should only be used in a single-threaded context. +However, nothing breaks if you do use it in a multithreaded context, that is, libikarus is threadsafe. +You just cannot rely on the data being consistent. +This goes especially for inter-process access to the same project. \ No newline at end of file diff --git a/docs/DoxyFile b/docs/DoxyFile new file mode 100644 index 0000000..61f3460 --- /dev/null +++ b/docs/DoxyFile @@ -0,0 +1,16 @@ +DISABLE_INDEX = NO +EXCLUDE = ../vendor ../build +EXCLUDE_PATTERNS = cmake-* +FILE_PATTERNS = *.h *.hpp *.tpp *.ipp *.cpp +FULL_SIDEBAR = NO +GENERATE_LATEX = NO +GENERATE_TREEVIEW = YES +HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5 +HTML_EXTRA_FILES = ../vendor/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js ../vendor/doxygen-awesome-css/doxygen-awesome-fragment-copy-button.js ./enum_format_fix.js +HTML_EXTRA_STYLESHEET = ../vendor/doxygen-awesome-css/doxygen-awesome.css +HTML_HEADER = header.html +INPUT = .. +OUTPUT_DIRECTORY = generated +PROJECT_BRIEF = A C-API implementation for Ikarus, a tool for worldbuilding. +PROJECT_NAME = LIBIKARUS +RECURSIVE = YES \ No newline at end of file diff --git a/docs/enum_format_fix.js b/docs/enum_format_fix.js new file mode 100644 index 0000000..2507ab8 --- /dev/null +++ b/docs/enum_format_fix.js @@ -0,0 +1,11 @@ +// maximum efficiency +function enumFormatFix() { + Array.from(document.getElementsByClassName("memItemRight")).forEach((elem) => { + elem.innerHTML = elem.innerHTML.replaceAll("
", ""); + elem.innerHTML = elem.innerHTML.replaceAll(" ", ""); + elem.innerHTML = elem.innerHTML.replaceAll("{", "{
    "); + elem.innerHTML = elem.innerHTML.replaceAll("\n,", ","); + elem.innerHTML = elem.innerHTML.replaceAll(",", ",
    "); + elem.innerHTML = elem.innerHTML.replaceAll("}", "
}"); + }); +} \ No newline at end of file diff --git a/docs/header.html b/docs/header.html new file mode 100644 index 0000000..dfce9a1 --- /dev/null +++ b/docs/header.html @@ -0,0 +1,97 @@ + + + + + + + + + $projectname: $title + $title + + + + + + + + + + + + + + + + + $treeview + $search + $mathjax + $darkmode + + $extrastylesheet + + + + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
$projectname $projectnumber + +
+ +
$projectbrief
+
+
$projectbrief
+
$searchbox
$searchbox
+
+ + diff --git a/implementation_details b/implementation_details new file mode 100644 index 0000000..42b38fc --- /dev/null +++ b/implementation_details @@ -0,0 +1,12 @@ +This list is intended to help keep the documentation up to date. +If you make changes to, for example, templates, always check the documentation for templates. +But sometimes information is shared. and referenced in multiple places. This helps keep track of that. + +Usage: Search for these keys prefixed with IMPLEMENTATION_DETAIL_* to change documentation in relevant places. + +DATABASE: References to the usage of a database +TREE_LAYOUT: References to our usage of a tree layout +OBJECT_TYPES: References to the types of objects +OBJECT_SCOPES: References to the usage of object scopes +PROPERTY_TYPES: The property types that currently exist +LAZY_VALUE_CREATION: The fact that values are created lazily \ No newline at end of file diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000..2e91ee9 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,8 @@ +file( + GLOB_RECURSE + FILES + "*.h" +) + +set(INCLUDE_FILES ${FILES} PARENT_SCOPE) + diff --git a/include/ikarus/macros.h b/include/ikarus/macros.h new file mode 100644 index 0000000..fdc6510 --- /dev/null +++ b/include/ikarus/macros.h @@ -0,0 +1,27 @@ +#pragma once + +#if defined(__unix__) + +#define IKA_OS_FAMILY_UNIX +#define IKA_API __attribute__((visibility("default"))) + +#if defined(linux) +#define IKA_OS_LINUX +#endif + +#elif defined(_WIN32) || defined(WIN32) +#define IKA_OS_WIN +#define IKA_API __declspec(dllexport) +#endif + +#ifndef IKA_API +#define IKA_API +#endif + +#ifdef __cplusplus +#define IKARUS_BEGIN_HEADER extern "C" { +#define IKARUS_END_HEADER } +#else +#define IKARUS_BEGIN_HEADER +#define IKARUS_END_HEADER +#endif diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h new file mode 100644 index 0000000..2f467cf --- /dev/null +++ b/include/ikarus/stdtypes.h @@ -0,0 +1,8 @@ +#pragma once + +#ifdef __cplusplus +#include +using std::size_t; +#else +#include +#endif diff --git a/include/ikarus/types/id.h b/include/ikarus/types/id.h new file mode 100644 index 0000000..a205e43 --- /dev/null +++ b/include/ikarus/types/id.h @@ -0,0 +1,58 @@ +// IMPLEMENTATION_DETAIL_DATABASE + +/// \file id.h +/// \author Folling + +#pragma once + +#include + +IKARUS_BEGIN_HEADER + +#include +#include + +/// \defgroup id Ids +/// \brief Ids are used to identify objects in the database. +/// \details They are stored as 64 bit integers with the following layout: +/// - first 8 bits: #IkarusObjectType - 255 possible values, 0 for special values +/// - last 56 bits: incremented counter generated by the database +/// @{ + +/// \brief A wrapper around a 64 bit integer that represents the id of an object. +/// \details They are stored as 64 bit integers with the following layout: +/// - first 8 bits: #IkarusObjectType - 255 possible values, 0 for special values +/// - last 56 bits: incremented counter generated by the database +struct IkarusId { + /// \private \brief The value of the id. + uint64_t value; +}; + +/// \brief A special id returned by failed functions. +IkarusId const IKARUS_ID_NONE{0}; +/// \brief A special id used to indicate an optional id not being specified. +IkarusId const IKARUS_ID_UNSPECIFIED{1}; + +/// \private \brief Generates a new id for the given object type. +/// \param object_type The type of the object to generate an id for. +/// \return The generated id. +IkarusId ikarus_id_from_integer(IkarusObjectType object_type); + +/// \brief Fetches the object type of the given id. +/// \param id The id to fetch the object type for. +/// \return The object type of the given id. +IKA_API IkarusObjectType ikarus_id_get_object_type(IkarusId id); + +/// \brief Checks if the given id is IKARUS_ID_NONE. +/// \param id The id to check. +/// \return True if the id is IKARUS_ID_NONE, false otherwise. +IKA_API bool ikarus_id_is_none(IkarusId id); + +/// \brief Checks if the given id is IKARUS_ID_UNSPECIFIED. +/// \param id The id to check. +/// \return True if the id is IKARUS_ID_UNSPECIFIED, false otherwise. +IKA_API bool ikarus_id_is_unspecified(IkarusId id); + +/// @} + +IKARUS_END_HEADER \ No newline at end of file diff --git a/include/ikarus/types/object.h b/include/ikarus/types/object.h new file mode 100644 index 0000000..9d8b9bd --- /dev/null +++ b/include/ikarus/types/object.h @@ -0,0 +1,164 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_OBJECT_TYPES +// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION +// IMPLEMENTATION_DETAIL_PROPERTY_TYPES + +/// \file id.h +/// \author Folling + +/// \defgroup object Objects +/// \brief Objects are a compound type of all types of objects in the database. +/// \details The following objects currently exist: +/// - blueprints +/// - properties +/// - entities +/// - blueprint folders +/// - property folders +/// - entity folders +/// @{ + +#include + +IKARUS_BEGIN_HEADER + +/// \brief A blueprint object. +/// \details A blueprint is a collection of properties which can be linked to entities. +/// Each entity the blueprint is linked to will have values for the blueprints properties. +struct IkarusBlueprint { + IkarusId id; +}; + +/// \brief Properties are the placeholders of values for entities. +/// \details Each entity can have any number of properties. +/// Every property has a type that identifies the kind of data that can be put in. +/// +/// 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) +/// +/// Every property has settings which can be used to customise the property further. +/// Two settings that are shared among all properties are the following: +/// - Multiple +/// - Allow undefined +/// +/// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. +/// The latter allows you to specify an "unknown" value for a property. +/// It might not be known if a character is dead or not for example. +/// +/// Each entity associated with the property has a value for it. +/// +/// Properties can also be added to blueprints in which case they are available for all entities associated with the +/// blueprint. +/// +/// We call properties within entities "Entity Properties" and properties within blueprints "Blueprint Properties". +/// +/// \remark Values for properties are lazily created as space saving measure. +/// Fetching the value for some property of some entity will return the property's default value if none is specified. +/// This default value is specified when the property is created and can be updated later. +/// +/// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. +struct IkarusProperty { + /// \private \brief The ID of the property. + IkarusId id; +}; + +/// \brief Entities are the core building blocks of Ikarus. +/// \detials Blueprints and Properties define the structure of the data. +/// Entities define the data itself. +/// +/// Properties can be associated with Entities in two ways: +/// - Directly: The property is linked to the entity. +/// - Indirectly: The property is linked to a blueprint of the entity. +/// +/// For each property an entity is linked to, it has a value. These values depend on the property's type. +/// For more information on the types see the property documentation. +/// +/// Values are the core type of data within Ikarus. +/// Each value is associated with one page and one property. +/// +/// \remark Values are typed, the type of a value is specified by its associated property. +/// For more information on the types see the property documentation. +/// +/// \remark Values are guaranteed to be in valid format for a given type +/// but not guaranteed to be valid under the settings of the property. +/// This is because changing the settings can invalidate existing values without resetting them. +struct IkarusEntity { + /// \private \brief The ID of the entity. + IkarusId id; +}; + +/// \brief A blueprint folder. +/// \see Folder +struct IkarusBlueprintFolder { + /// \private \brief The ID of the folder. + IkarusId id; +}; + +/// \brief A property folder. +/// \remark Property folders are scoped to the blueprint or entity they are associated with. +/// \see Folder +struct IkarusPropertyFolder { + /// \private \brief The ID of the folder. + IkarusId id; +}; + +/// \brief An entity folder. +/// \see Folder +struct IkarusEntityFolder { + /// \private \brief The ID of the folder. + IkarusId id; +}; + +/// \private \brief The data of a folder. +union IkarusFolderData { + /// \private \brief The blueprint folder data of the folder. + IkarusBlueprintFolder blueprint_folder; + /// \private \brief The property folder data of the folder. + IkarusPropertyFolder property_folder; + /// \private \brief The entity folder data of the folder. + IkarusEntityFolder entity_folder; +}; + +/// \brief A generic folder. Similar to how Objects wrap all types of objects, Folders wrap all types of folders. +struct IkarusFolder { + /// \private \brief The data of the folder. + IkarusFolderData data; + + /// \private \brief The type of the folder. + IkarusFolderType type; +}; + +/// \private \brief The data of an object. +union IkarusObjectData { + /// \private \brief The blueprint data of the object. + IkarusBlueprint blueprint; + /// \private \brief The property data of the object. + IkarusProperty property; + /// \private \brief The entity data of the object. + IkarusEntity entity; + /// \private \brief The blueprint folder data of the object. + IkarusBlueprintFolder blueprint_folder; + /// \private \brief The property folder data of the object. + IkarusPropertyFolder property_folder; + /// \private \brief The entity folder data of the object. + IkarusEntityFolder entity_folder; +}; + +/// \brief A generic object. Wraps all types of objects, including folders. +struct IkarusObject { + /// \private \brief The data of the object. + IkarusObjectData data; + /// \private \brief The type of the object. + IkarusObjectType type; +}; + +// @} + +IKARUS_END_HEADER diff --git a/include/ikarus/types/object_scope.h b/include/ikarus/types/object_scope.h new file mode 100644 index 0000000..1fe51e1 --- /dev/null +++ b/include/ikarus/types/object_scope.h @@ -0,0 +1,149 @@ +// IMPLEMENTATION_DETAIL_OBJECT_SCOPES, IMPLEMENTATION_DETAIL_TREE_LAYOUT + +/// \file object_scope.h +/// \author Folling + +/// \defgroup object_scopes Object Scopes +/// \brief Scopes define where objects belong to. +/// \details They are required to differentiate between different types of objects with NULL as their parent. +/// @{ + +#pragma once + +#include +#include + +IKARUS_BEGIN_HEADER + +/// \brief The global scope of all blueprints. +struct IkarusBlueprintScope { + /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. + short _dummy; +}; + +/// \brief Data for a property scope. This can either be a blueprint or an entity. +union IkarusPropertyScopeData { + /// \private \brief The blueprint the property is scoped to. + IkarusBlueprint _blueprint; + /// \private \brief The entity the property is scoped to. + IkarusEntity _entity; +}; + +/// \brief The type of a property scope. This can either be a blueprint or an entity. +enum IkarusPropertyScopeType { + /// \brief The property is scoped to a blueprint. + IkarusPropertyScopeType_Blueprint, + /// \brief The property is scoped to an entity. + IkarusPropertyScopeType_Entity +}; + +/// \brief The scope of a property +struct IkarusPropertyScope { + /// \private \brief Represents the type of the scope. + IkarusPropertyScopeType _type; + /// \private \brief Represents the data of the scope. + IkarusPropertyScopeData _data; +}; + +/// The global scope of all entities. +struct IkarusEntityScope { + /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. + short _dummy; +}; + +/// \private \brief The data for an object scope. +union IkarusObjectScopeData { + /// \private \brief The blueprint data of the scope. + IkarusBlueprintScope _blueprint; + /// \private \brief The property data of the scope. + IkarusPropertyScope _property; + /// \private \brief The entity data of the scope. + IkarusEntityScope _entity; +}; + +/// The type of an object scope. +enum IkarusObjectScopeType { + /// \brief The scope is a blueprint scope. + IkarusObjectScopeType_Blueprint, + /// \brief The scope is a property scope. + IkarusObjectScopeType_Property, + /// \brief The scope is an entity scope. + IkarusObjectScopeType_Entity +}; + +/// \brief The scope of an object. +struct IkarusObjectScope { + /// \private \brief Represents the type of the scope. + IkarusObjectScopeType _type; + /// \private \brief Represents the data of the scope. + IkarusObjectScopeData _data; +}; + +/// \brief Creates a blueprint scope. +/// \return The created blueprint scope. +IKA_API IkarusBlueprintScope ikarus_blueprint_scope_create(); +/// \brief Converts a blueprint scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope); + +/// \brief Creates a property scope from a blueprint. +/// \param blueprint The blueprint the property is scoped to. +/// \return The created property scope. +IKA_API IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint const * blueprint); +/// \brief Creates a property scope from a entity. +/// \param entity The entity the property is scoped to. +/// \return The created property scope. +IKA_API IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity const * entity); +/// \brief Converts a property scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope); + +/// \brief Fetches the type of an property scope. +/// \param scope The scope to fetch the type of. +/// \return The type of the scope. +IKA_API IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope const * scope); + +/// \brief Visits a property scope, calling the appropriate function. +/// \param scope The scope to to visit +/// \param blueprint The function to call if the property is scoped to a blueprint. +/// \param entity The function to call if the property is scoped to an entity. +/// \param data Optional data to pass to the functions. +void ikarus_property_scope_visit( + IkarusPropertyScope const * scope, + void (*blueprint)(IkarusBlueprint const *, void *), + void (*entity)(IkarusEntity const *, void *), + void * data +); + +/// \brief Creates an entity scope. +/// \return The created entity scope. +IKA_API IkarusEntityScope ikarus_entity_scope_create(); +/// Converts an entity scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope); + +/// \brief Fetches the type of an object scope. +/// \param scope The scope to fetch the type of. +/// \return The type of the scope. +IKA_API IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope const * scope); + +/// \brief Visits an object scope, calling the appropriate function. +/// \param scope The scope to visit. +/// \param blueprint The function to call if the scope is an #IkarusBlueprintScope. +/// \param property The function to call if the scope is an #IkarusPropertyScope. +/// \param entity The function to call if the scope is an #IkarusEntityScope. +/// \remark function pointers may be null in which case they are not called. +IKA_API void ikarus_object_scope_visit( + IkarusObjectScope const * scope, + void (*blueprint)(IkarusBlueprintScope const *, void *), + void (*property)(IkarusPropertyScope const *, void *), + void (*entity)(IkarusEntityScope const *, void *), + void * data +); + +/// @} + +IKARUS_END_HEADER diff --git a/include/ikarus/types/object_type.h b/include/ikarus/types/object_type.h new file mode 100644 index 0000000..ca3dfb0 --- /dev/null +++ b/include/ikarus/types/object_type.h @@ -0,0 +1,63 @@ +#pragma once + +#include + +IKARUS_BEGIN_HEADER + +/// \defgroup object_types ObjectTypes +/// \brief ObjectTypes are used to identify the type of objects. +/// @{ + +/// \brief The type of a folder. +/// \remark folders have the first bit set and then mirror the object type of the underlying object +enum IkarusFolderType { + /// \brief Not a folder or no folder. + IkarusFolderType_None = 0, + /// \brief An IkarusBlueprintFolder + IkarusFolderType_BlueprintFolder = 0b1000'0001, + /// \brief An IkarusPropertyFolder + IkarusFolderType_PropertyFolder = 0b1000'0010, + /// \brief An IkarusEntityFolder + IkarusFolderType_EntityFolder = 0b1000'0011, +}; + +/// \brief The type of an object. +/// \remark folders have the first bit set and then mirror the object type of the underlying object +enum IkarusObjectType { + /// \brief Not an object or no object. + ObjectType_None = 0, + /// \brief An IkarusBlueprint. + ObjectType_Blueprint = 0b0000'0001, + /// \brief An IkarusProperty. + ObjectType_Property = 0b0000'0010, + /// \brief An IkarusEntity. + ObjectType_Entity = 0b0000'0011, + /// \brief An IkarusBlueprintFolder + ObjectType_BlueprintFolder = 0b1000'0001, + /// \brief An IkarusPropertyFolder + ObjectType_PropertyFolder = 0b1000'0010, + /// \brief An IkarusEntityFolder + ObjectType_EntityFolder = 0b1000'0011, +}; + +/// \brief A bitset of IkarusObjectType%s. +enum ObjectTypes { + /// \brief No object type. + ObjectTypes_None = 0, + /// \brief An IkarusBlueprint. + ObjectTypes_Blueprint = 1 << ObjectType_Blueprint, + /// \brief An IkarusProperty. + ObjectTypes_Property = 1 << ObjectType_Property, + /// \brief An IkarusEntity. + ObjectTypes_Entity = 1 << ObjectType_Entity, + /// \brief An IkarusBlueprintFolder + ObjectTypes_BlueprintFolder = 1 << ObjectType_BlueprintFolder, + /// \brief An IkarusPropertyFolder + ObjectTypes_PropertyFolder = 1 << ObjectType_PropertyFolder, + /// \brief An IkarusEntityFolder + ObjectTypes_EntityFolder = 1 << ObjectType_EntityFolder, +}; + +// @} + +IKARUS_END_HEADER \ No newline at end of file diff --git a/include/ikarus/types/property_type.h b/include/ikarus/types/property_type.h new file mode 100644 index 0000000..d22e1a6 --- /dev/null +++ b/include/ikarus/types/property_type.h @@ -0,0 +1,31 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_PROPERTY_TYPES + +/// \file id.h +/// \author Folling + +#include + +IKARUS_BEGIN_HEADER + +/// \defgroup property_types Property Types +/// \brief Property Types delineate the type of data stored by a property. +/// @{ + +/// \brief The type of a property. +/// \details Designates the type of data stored by the property as well as which settings are +/// available. +/// \see IkarusPropertySettings +enum IkarusPropertyType { + /// \brief A true/false boolean-like value. + IkarusPropertyType_Toggle, + /// \brief An arbitrary numeric value. + IkarusPropertyType_Number, + /// \brief An arbitrary textual value. + IkarusPropertyType_Text, +}; + +/// @} + +IKARUS_END_HEADER diff --git a/include/ikarus/types/value.h b/include/ikarus/types/value.h new file mode 100644 index 0000000..d636d27 --- /dev/null +++ b/include/ikarus/types/value.h @@ -0,0 +1,161 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_PROPERTY_TYPES + +#include +#include +#include + +IKARUS_BEGIN_HEADER + +/// \defgroup entity_value Entity Values +/// \brief The values stored in entities. +/// \details Each entity has a value for each property it is associated with. +/// The value is of the type specified by the property and constrained by the property's settings. +/// \see PropertyType PropertySettings +/// @{ + +/// \brief A true/false boolean-like value. For example "IsDead". +struct IkarusToggleValue { + /// \private \brief The value of the property. + bool _value; +}; + +/// \brief An arbitrary numeric value. For example "Age". +struct IkarusNumberValue { + /// \private \brief The value of the property. + long double _value; +}; + +/// \brief An arbitrary textual value. For example "First Name". +struct IkarusTextValue { + /// \private \brief The value of the property. + char const * _value; +}; + +/// \private \brief The data for a value. +union IkarusEntityValueData { + /// \private \brief The value as a toggle. + IkarusToggleValue toggle; + /// \private \brief The value as a number. + IkarusNumberValue number; + /// \private \brief The value as text. + IkarusTextValue text; +}; + +/// \brief The state of an entity value. +/// \details States provide insight into the nature of a value. +enum IkarusEntityValueState { + /// \brief The value is invalid. + IkarusEntityValueState_Invalid, + /// \brief The value is normal and can be used as-is. + IkarusEntityValueState_Normal, + /// \brief The value is unknown. + IkarusEntityValueState_Indeterminate, +}; + +/// \brief The value of an entity associated with a property. +struct IkarusEntityValue { + /// \private \brief The type of the value. + IkarusPropertyType _type; + /// \private \brief The data for the value.p + IkarusEntityValueData _data; + /// \private \brief The state of the value. + IkarusEntityValueState _state; +}; + +/// \brief Creates an entity value from a toggle value. +/// \param value The toggle value. +/// \return The entity value. +IKA_API IkarusEntityValue ikarus_value_create_toggle(bool value); +/// \brief Creates an entity value from a number value. +/// \param value The number value. +/// \return The entity value. +/// \remark If the value is NaN or infinity an InvalidEntityValue is returned. +IKA_API IkarusEntityValue ikarus_value_create_number(long double value); +/// \brief Creates an entity value from a text value. +/// \param value The text value. +/// \return The entity value. +/// \remark If the value is null an InvalidEntityValue is returned. +IKA_API IkarusEntityValue ikarus_value_create_text(char const * value); + +/// \brief Creates an indeterminate entity value of a given type. +/// \param type The type of the value. +/// \return The entity value. +IKA_API IkarusEntityValue ikarus_value_create_indeterminate(IkarusPropertyType type); + +/// \brief Fetches the default value for a property type. +/// \remark Not to be confused with the default value of a property. See ikarus_property_get_default_value +/// \param type The property type. +/// \return The default value for the property type. +IKA_API IkarusEntityValue ikarus_value_get_default(IkarusPropertyType type); + +/// \brief Fetches the underlying value of a toggle value. +/// \param value The toggle value. +/// \return The underlying value. +IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value); + +/// \brief Fetches the underlying value of a number value. +/// \param value The number value. +/// \return The underlying value. +IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value); + +/// \brief Fetches the underlying value of a text value. +/// \param value The text value. +/// \return The underlying value. +IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); + +/// \brief Checks if a toggle value is equal to a boolean. +/// \param value The toggle value. +/// \param check The boolean value. +/// \return False if value is null. True if it is equal to check, false otherwise. +IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * value, bool check); + +/// \brief Checks if a number value is equal to a number. +/// \param value The number value. +/// \param check The number value. +/// \return False if value is null. True if it is equal to check, false otherwise. +IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * value, long double check); + +/// \brief Checks if a text value is equal to a string. +/// \param value The text value. +/// \param check The string value. +/// \return False if value or check are null. True if it is equal to check, false otherwise. +IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * value, char const * check); + +/// \brief Checks if two entity values are equal. +/// \details Two entity values are equal if they are of the same type and their value is considered equal. +/// Note that floating point values can only be checked for approximate equality. +/// \param left The left-hand entity value. +/// \param right The right-hand entity value. +/// \return True if the values are considered equal, false otherwise. +/// \remark Null values compare false to all other values. As do invalid values. Indeterminate values however, compare true to +/// other indeterminate values of the same type. +IKA_API bool ikarus_value_is_equal(IkarusEntityValue const * left, IkarusEntityValue const * right); + +/// \brief Checks if an entity value is invalid. +/// \param value The entity value. +/// \return True if the value is invalid or null, false otherwise. +IKA_API bool ikarus_value_is_invalid(IkarusEntityValue const * value); + +/// \brief Fetches the type of an entity value. +/// \param value The entity value. +/// \return The type of the entity value. +IKA_API IkarusPropertyType ikarus_value_get_type(IkarusEntityValue const * value); + +/// \brief Visits an entity value, calling the appropriate function for the value's type. +/// \param value The entity value to visit. +/// \param toggle The function to call if the value is a toggle value. +/// \param number The function to call if the value is a number value. +/// \param text The function to call if the value is a text value. +/// \param data The data to pass to the functions. +/// \remark function pointers may be null in which case they are not called. +IKA_API void ikarus_value_visit( + IkarusEntityValue const * value, + void (*toggle)(IkarusToggleValue const *, void *), + void (*number)(IkarusNumberValue const *, void *), + void (*text)(IkarusTextValue const *, void *), + void * data +); + +IKARUS_END_HEADER diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..a0665aa --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,7 @@ +file( + GLOB_RECURSE + FILES + "*.cpp" +) + +set(SOURCE_FILES ${FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/types/object_scope.cpp b/src/types/object_scope.cpp new file mode 100644 index 0000000..a4f2b49 --- /dev/null +++ b/src/types/object_scope.cpp @@ -0,0 +1,209 @@ +#include "ikarus/types/object_scope.h" + +#include + +#include + +IkarusBlueprintScope ikarus_blueprint_scope_create() { + return IkarusBlueprintScope{._dummy = 0}; +} + +IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope) { + IkarusObjectScopeData data{}; + data._blueprint = *scope; + + return IkarusObjectScope{._type = IkarusObjectScopeType_Blueprint, ._data = data}; +} + +IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint const * blueprint) { + IkarusPropertyScopeData data{}; + data._blueprint = *blueprint; + return IkarusPropertyScope{._type = IkarusPropertyScopeType_Blueprint, ._data = data}; +} + +IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity const * entity) { + IkarusPropertyScopeData data{}; + data._entity = *entity; + return IkarusPropertyScope{._type = IkarusPropertyScopeType_Entity, ._data = data}; +} + +IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope) { + IkarusObjectScopeData data{}; + data._property = *scope; + + return IkarusObjectScope{._type = IkarusObjectScopeType_Property, ._data = data}; +} + +IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope const * scope) { + return scope->_type; +} + +void ikarus_property_scope_visit( + IkarusPropertyScope const * scope, + void (*blueprint)(IkarusBlueprint const *, void *), + void (*entity)(IkarusEntity const *, void *), + void * data +) { + switch (scope->_type) { + case IkarusPropertyScopeType_Blueprint: blueprint(&scope->_data._blueprint, data); break; + case IkarusPropertyScopeType_Entity: entity(&scope->_data._entity, data); break; + } +} + +IkarusEntityScope ikarus_entity_scope_create() { + return IkarusEntityScope{._dummy = 0}; +} + +IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope) { + IkarusObjectScopeData data{}; + data._entity = *scope; + + return IkarusObjectScope{._type = IkarusObjectScopeType_Entity, ._data = data}; +} + +IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope const * scope) { + return scope->_type; +} + +void ikarus_object_scope_visit( + IkarusObjectScope const * scope, + void (*blueprint)(IkarusBlueprintScope const *, void *), + void (*property)(IkarusPropertyScope const *, void *), + void (*entity)(IkarusEntityScope const *, void *), + void * data +) { + switch (scope->_type) { + case IkarusObjectScopeType_Blueprint: { + if (blueprint != nullptr) { + blueprint(&scope->_data._blueprint, data); + } + break; + } + case IkarusObjectScopeType_Property: { + if (property != nullptr) { + property(&scope->_data._property, data); + } + break; + } + case IkarusObjectScopeType_Entity: { + if (entity != nullptr) { + entity(&scope->_data._entity, data); + } + break; + } + } +} + +TEST_CASE("blueprint_object_scope_conversion", "[object_scope]") { + auto blueprint_scope = ikarus_blueprint_scope_create(); + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); + REQUIRE(blueprint_object_scope._type == IkarusObjectScopeType_Blueprint); +} + +TEST_CASE("property_scope_type", "[object_scope]") { + auto blueprint = IkarusBlueprint{}; + auto entity = IkarusEntity{}; + + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto property_entity_scope = ikarus_property_scope_create_entity(&entity); + + REQUIRE(ikarus_property_scope_get_type(&property_blueprint_scope) == IkarusPropertyScopeType_Blueprint); + REQUIRE(ikarus_property_scope_get_type(&property_entity_scope) == IkarusPropertyScopeType_Entity); +} + +TEST_CASE("property_object_scope_conversion", "[object_scope]") { + auto blueprint = IkarusBlueprint{}; + auto entity = IkarusEntity{}; + + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto property_blueprint_object_scope = ikarus_property_scope_to_object_scope(&property_blueprint_scope); + + REQUIRE(property_blueprint_object_scope._type == IkarusObjectScopeType_Property); + + auto property_entity_scope = ikarus_property_scope_create_entity(&entity); + auto property_entity_object_scope = ikarus_property_scope_to_object_scope(&property_entity_scope); + + REQUIRE(property_entity_object_scope._type == IkarusObjectScopeType_Property); +} + +TEST_CASE("property_scope_visiting", "[object_scope]") { + auto blueprint = IkarusBlueprint{}; + auto entity = IkarusEntity{}; + + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto property_entity_scope = ikarus_property_scope_create_entity(&entity); + + int test = 0; + + ikarus_property_scope_visit( + &property_blueprint_scope, + [](IkarusBlueprint const * _, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusEntity const * _, void * data) { *reinterpret_cast(data) = 2; }, + &test + ); + + REQUIRE(test == 1); + + ikarus_property_scope_visit( + &property_entity_scope, + [](IkarusBlueprint const * _, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusEntity const * _, void * data) { *reinterpret_cast(data) = 2; }, + &test + ); + + REQUIRE(test == 2); +} + +TEST_CASE("entity_object_scope_conversion", "[object_scope]") { + auto entity_scope = ikarus_entity_scope_create(); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + REQUIRE(entity_object_scope._type == IkarusObjectScopeType_Entity); +} + +TEST_CASE("object_scope_type_fetching", "[object_scope]") { + auto blueprint = IkarusBlueprint{}; + + auto blueprint_scope = ikarus_blueprint_scope_create(); + auto property_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto entity_scope = ikarus_entity_scope_create(); + + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); + auto property_object_scope = ikarus_property_scope_to_object_scope(&property_scope); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + + REQUIRE(ikarus_object_scope_get_type(&blueprint_object_scope) == IkarusObjectScopeType_Blueprint); + REQUIRE(ikarus_object_scope_get_type(&property_object_scope) == IkarusObjectScopeType_Property); + REQUIRE(ikarus_object_scope_get_type(&entity_object_scope) == IkarusObjectScopeType_Entity); +} + +TEST_CASE("object_scope_visiting", "[object_scope]") { + auto blueprint = IkarusBlueprint{}; + + auto blueprint_scope = ikarus_blueprint_scope_create(); + auto property_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto entity_scope = ikarus_entity_scope_create(); + + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); + auto property_object_scope = ikarus_property_scope_to_object_scope(&property_scope); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + + auto scopes = { + std::make_pair(blueprint_object_scope, 1), + std::make_pair(property_object_scope, 2), + std::make_pair(entity_object_scope, 3), + }; + + for (auto [scope, value] : scopes) { + int test = 0; + + ikarus_object_scope_visit( + &scope, + [](IkarusBlueprintScope const * _, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusPropertyScope const * _, void * data) { *reinterpret_cast(data) = 2; }, + [](IkarusEntityScope const * _, void * data) { *reinterpret_cast(data) = 3; }, + &test + ); + + REQUIRE(test == value); + } +} diff --git a/src/types/value.cpp b/src/types/value.cpp new file mode 100644 index 0000000..251db2c --- /dev/null +++ b/src/types/value.cpp @@ -0,0 +1,337 @@ +#include "ikarus/types/value.h" + +#include +#include + +#include + +/// \brief Creates an indeterminate entity value of a given type. +/// \param type The type of the value. +/// \return The entity value. +IKA_API IkarusEntityValue value_create_invalid(IkarusPropertyType type) { + return IkarusEntityValue{ + ._type = type, + ._data = IkarusEntityValueData{}, + ._state = IkarusEntityValueState_Invalid, + }; +} + +IkarusEntityValue ikarus_value_create_toggle(bool value) { + return IkarusEntityValue{ + ._type = IkarusPropertyType_Toggle, + ._data = IkarusEntityValueData{.toggle = IkarusToggleValue{._value = value}}, + ._state = IkarusEntityValueState_Normal, + }; +} + +IkarusEntityValue ikarus_value_create_number(long double value) { + if (auto fp_class = std::fpclassify(value); fp_class != FP_NORMAL && fp_class != FP_ZERO) { + return value_create_invalid(IkarusPropertyType_Number); + } + + return IkarusEntityValue{ + ._type = IkarusPropertyType_Number, + ._data = IkarusEntityValueData{.number = IkarusNumberValue{._value = value}}, + ._state = IkarusEntityValueState_Normal, + }; +} + +IkarusEntityValue ikarus_value_create_text(char const * value) { + if (value == nullptr) { + return value_create_invalid(IkarusPropertyType_Text); + }; + + return IkarusEntityValue{ + ._type = IkarusPropertyType_Text, + ._data = IkarusEntityValueData{.text = IkarusTextValue{._value = value}}, + ._state = IkarusEntityValueState_Normal, + }; +} + +IkarusEntityValue ikarus_value_create_indeterminate(IkarusPropertyType type) { + IkarusEntityValueData data{}; + + switch (type) { + case IkarusPropertyType_Toggle: { + data.toggle = IkarusToggleValue{._value = false}; + break; + } + case IkarusPropertyType_Number: { + data.number = IkarusNumberValue{._value = 0.0}; + break; + } + case IkarusPropertyType_Text: { + data.text = IkarusTextValue{._value = ""}; + break; + } + default: return value_create_invalid(type); + } + + return IkarusEntityValue{ + ._type = type, + ._data = data, + ._state = IkarusEntityValueState_Indeterminate, + }; +} + +IkarusEntityValue ikarus_value_get_default(IkarusPropertyType type) { + switch (type) { + case IkarusPropertyType_Toggle: return ikarus_value_create_toggle(false); + case IkarusPropertyType_Number: return ikarus_value_create_number(0.0); + case IkarusPropertyType_Text: return ikarus_value_create_text(""); + default: return value_create_invalid(type); + } +} + +bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value) { + return value->_value; +} + +long double ikarus_number_value_get_underlying(IkarusNumberValue const * value) { + return value->_value; +} + +char const * ikarus_text_value_get_underlying(IkarusTextValue const * value) { + return value->_value; +} + +// no need to check for validity here, since these concrete types are only created by the library +bool ikarus_toggle_value_is_equal(IkarusToggleValue const * value, bool check) { + return value != nullptr && value->_value == check; +} + +bool ikarus_number_value_is_equal(IkarusNumberValue const * value, long double check) { + return value != nullptr && value->_value == check; +} + +bool ikarus_text_value_is_equal(IkarusTextValue const * value, char const * check) { + return value != nullptr && check != nullptr && std::strcmp(value->_value, check) == 0; +} + +bool ikarus_value_is_equal(IkarusEntityValue const * left, IkarusEntityValue const * right) { + if (left == nullptr || right == nullptr) { + return false; + } + + if (left->_state == IkarusEntityValueState_Invalid || right->_state == IkarusEntityValueState_Invalid) { + return false; + } + + if (left->_type != right->_type) { + return false; + } + + // indeterminate values are only equal if they have the same type + if (left->_state == IkarusEntityValueState_Indeterminate && right->_state == IkarusEntityValueState_Indeterminate) { + return true; + } + + switch (left->_type) { + case IkarusPropertyType_Toggle: return left->_data.toggle._value == right->_data.toggle._value; + case IkarusPropertyType_Number: return left->_data.number._value == right->_data.number._value; + case IkarusPropertyType_Text: return std::strcmp(left->_data.text._value, right->_data.text._value) == 0; + default: return false; + } +} + +bool ikarus_value_is_invalid(IkarusEntityValue const * value) { + return value == nullptr || value->_state == IkarusEntityValueState_Invalid; +} + +IkarusPropertyType ikarus_value_get_type(IkarusEntityValue const * value) { + return value->_type; +} + +void ikarus_value_visit( + IkarusEntityValue const * value, + void (*toggle)(IkarusToggleValue const * value, void * data), + void (*number)(IkarusNumberValue const * value, void * data), + void (*text)(IkarusTextValue const * value, void * data), + void * data +) { + if (value == nullptr) { + return; + } + + switch (value->_type) { + case IkarusPropertyType_Toggle: { + if (toggle != nullptr) { + toggle(&value->_data.toggle, data); + } + break; + } + case IkarusPropertyType_Number: { + if (number != nullptr) { + number(&value->_data.number, data); + } + break; + } + case IkarusPropertyType_Text: { + if (text != nullptr) { + text(&value->_data.text, data); + } + break; + } + default: break; + } +} + +TEST_CASE("toggle_value_creation", "[value]") { + auto toggle_value = ikarus_value_create_toggle(true); + + REQUIRE(ikarus_value_get_type(&toggle_value) == IkarusPropertyType_Toggle); + REQUIRE(ikarus_toggle_value_is_equal(&toggle_value._data.toggle, true)); +} + +TEST_CASE("number_value_creation", "[value]") { + auto number_value = ikarus_value_create_number(1.0); + + REQUIRE(ikarus_value_get_type(&number_value) == IkarusPropertyType_Number); + REQUIRE(ikarus_number_value_is_equal(&number_value._data.number, 1.0)); + + auto nan_value = ikarus_value_create_number(std::numeric_limits::quiet_NaN()); + REQUIRE(ikarus_value_is_invalid(&nan_value)); + auto signaling_non_value = ikarus_value_create_number(std::numeric_limits::signaling_NaN()); + REQUIRE(ikarus_value_is_invalid(&signaling_non_value)); + auto inf_value = ikarus_value_create_number(std::numeric_limits::infinity()); + REQUIRE(ikarus_value_is_invalid(&inf_value)); + auto neg_inf_value = ikarus_value_create_number(-std::numeric_limits::infinity()); + REQUIRE(ikarus_value_is_invalid(&neg_inf_value)); +} + +TEST_CASE("text_value_creation", "[value]") { + auto text_value = ikarus_value_create_text("test"); + + REQUIRE(ikarus_value_get_type(&text_value) == IkarusPropertyType_Text); + REQUIRE(ikarus_text_value_is_equal(&text_value._data.text, "test")); + + auto null_value = ikarus_value_create_text(nullptr); + REQUIRE(ikarus_value_is_invalid(&null_value)); +} + +TEST_CASE("default_value_creation", "[value]") { + auto types = { + IkarusPropertyType_Toggle, + IkarusPropertyType_Number, + IkarusPropertyType_Text, + }; + + for (auto type : types) { + auto value = ikarus_value_get_default(type); + REQUIRE(ikarus_value_get_type(&value) == type); + } +} + +TEST_CASE("toggle_value_underlying", "[value]") { + auto true_toggle_value = ikarus_value_create_toggle(true); + auto false_toggle_value = ikarus_value_create_toggle(false); + + REQUIRE(ikarus_toggle_value_get_underlying(&true_toggle_value._data.toggle) == true); + REQUIRE(ikarus_toggle_value_get_underlying(&false_toggle_value._data.toggle) == false); +} + +TEST_CASE("number_value_underlying", "[value]") { + auto zero_number_value = ikarus_value_create_number(0.0); + auto third_number_value = ikarus_value_create_number(1.0 / 3.0); + auto large_number_value = ikarus_value_create_number(1.2345678910e123); + + REQUIRE(ikarus_number_value_get_underlying(&zero_number_value._data.number) == 0.0); + REQUIRE(ikarus_number_value_get_underlying(&third_number_value._data.number) == 1.0 / 3.0); + REQUIRE(ikarus_number_value_get_underlying(&large_number_value._data.number) == 1.2345678910e123); +} + +TEST_CASE("text_value_underlying", "[value]") { + auto test_value = ikarus_value_create_text("test"); + auto empty_value = ikarus_value_create_text(""); + + REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&test_value._data.text), "test") == 0); + REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&empty_value._data.text), "") == 0); +} + +TEST_CASE("toggle_comparison", "[value]") { + auto true_toggle_value = ikarus_value_create_toggle(true); + auto false_toggle_value = ikarus_value_create_toggle(false); + + REQUIRE(ikarus_toggle_value_is_equal(&true_toggle_value._data.toggle, true)); + REQUIRE(ikarus_toggle_value_is_equal(&false_toggle_value._data.toggle, false)); +} + +TEST_CASE("number_comparison", "[value]") { + auto zero_number_value = ikarus_value_create_number(0.0); + auto third_number_value = ikarus_value_create_number(1.0 / 3.0); + auto large_number_value = ikarus_value_create_number(1.2345678910e123); + + REQUIRE(ikarus_number_value_is_equal(&zero_number_value._data.number, 0.0)); + REQUIRE(ikarus_number_value_is_equal(&third_number_value._data.number, 1.0 / 6.0 + 1.0 / 6.0)); + REQUIRE(ikarus_number_value_is_equal(&large_number_value._data.number, 1.2345678910e123)); +} + +TEST_CASE("text_comparison", "[value]") { + auto test_value = ikarus_value_create_text("test"); + auto empty_value = ikarus_value_create_text(""); + + REQUIRE(ikarus_text_value_is_equal(&test_value._data.text, "test")); + REQUIRE(ikarus_text_value_is_equal(&empty_value._data.text, "")); +} + +TEST_CASE("value_comparison", "[value]") { + auto true_toggle_value = ikarus_value_create_toggle(true); + auto false_toggle_value = ikarus_value_create_toggle(false); + auto number_value1 = ikarus_value_create_number(0.0); + auto number_value2 = ikarus_value_create_number(0.0); + auto invalid_value = ikarus_value_create_text(nullptr); + + auto indeterminate_toggle = ikarus_value_create_indeterminate(IkarusPropertyType_Toggle); + auto indeterminate_number1 = ikarus_value_create_indeterminate(IkarusPropertyType_Number); + auto indeterminate_number2 = ikarus_value_create_indeterminate(IkarusPropertyType_Number); + + REQUIRE(!ikarus_value_is_equal(nullptr, nullptr)); + REQUIRE(!ikarus_value_is_equal(&true_toggle_value, nullptr)); + REQUIRE(!ikarus_value_is_equal(nullptr, &true_toggle_value)); + + REQUIRE(!ikarus_value_is_equal(&invalid_value, &invalid_value)); + REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &invalid_value)); + REQUIRE(!ikarus_value_is_equal(&invalid_value, &true_toggle_value)); + + REQUIRE(ikarus_value_is_equal(&true_toggle_value, &true_toggle_value)); + REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &false_toggle_value)); + REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &number_value1)); + REQUIRE(!ikarus_value_is_equal(&number_value1, &true_toggle_value)); + REQUIRE(ikarus_value_is_equal(&number_value1, &number_value2)); + + REQUIRE(!ikarus_value_is_equal(&indeterminate_toggle, &indeterminate_number1)); + REQUIRE(ikarus_value_is_equal(&indeterminate_number1, &indeterminate_number2)); +} + +TEST_CASE("invalid_value", "[value]") { + auto invalid_value = ikarus_value_create_toggle(false); + invalid_value._state = IkarusEntityValueState_Invalid; + + REQUIRE(ikarus_value_is_invalid(&invalid_value)); +} + +TEST_CASE("visit_value", "[value]") { + auto toggle_value = ikarus_value_create_toggle(true); + auto number_value = ikarus_value_create_number(0.0); + auto text_value = ikarus_value_create_text("test"); + + auto values = { + std::make_pair(toggle_value, 1), + std::make_pair(number_value, 2), + std::make_pair(text_value, 3), + }; + + for (auto [value, expected] : values) { + int test = 0; + + ikarus_value_visit( + &value, + [](IkarusToggleValue const * _, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusNumberValue const * _, void * data) { *reinterpret_cast(data) = 2; }, + [](IkarusTextValue const * _, void * data) { *reinterpret_cast(data) = 3; }, + &test + ); + + REQUIRE(test == expected); + } +} diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt new file mode 100644 index 0000000..9820477 --- /dev/null +++ b/vendor/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(catch2) +add_subdirectory(sqlitecpp) diff --git a/vendor/catch2 b/vendor/catch2 new file mode 160000 index 0000000..5bba3e4 --- /dev/null +++ b/vendor/catch2 @@ -0,0 +1 @@ +Subproject commit 5bba3e4038602badb691da914523f667a2dd1f27 diff --git a/vendor/doxygen-awesome-css b/vendor/doxygen-awesome-css new file mode 160000 index 0000000..00a52f6 --- /dev/null +++ b/vendor/doxygen-awesome-css @@ -0,0 +1 @@ +Subproject commit 00a52f6c74065ffbd836cbd791ddfe8edf2836b8 diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp new file mode 160000 index 0000000..afe7b16 --- /dev/null +++ b/vendor/sqlitecpp @@ -0,0 +1 @@ +Subproject commit afe7b165002ccf86a37da5b6b157ce4ff9db0401 From b0a31a2140e285cdb8bd68f41fc8a42d2b07dcb4 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 22 Aug 2023 20:37:22 +0200 Subject: [PATCH 085/166] add license Signed-off-by: Folling --- LICENSE.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..598a8d4 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,10 @@ +Copyright 2019-2023 Folling (mail@folling.io) + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + From d43fe3e0b40bf76354b93473c6ef49556182b03a Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 24 Aug 2023 16:40:54 +0200 Subject: [PATCH 086/166] id interface Signed-off-by: Folling --- include/ikarus/stdtypes.h | 1 + include/ikarus/types/id.h | 19 +++++++--- include/ikarus/types/object_type.h | 28 +++++++-------- src/types/id.cpp | 56 ++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 src/types/id.cpp diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h index 2f467cf..d5f7ab2 100644 --- a/include/ikarus/stdtypes.h +++ b/include/ikarus/stdtypes.h @@ -1,6 +1,7 @@ #pragma once #ifdef __cplusplus +#include #include using std::size_t; #else diff --git a/include/ikarus/types/id.h b/include/ikarus/types/id.h index a205e43..e2ca0b5 100644 --- a/include/ikarus/types/id.h +++ b/include/ikarus/types/id.h @@ -6,10 +6,10 @@ #pragma once #include +#include IKARUS_BEGIN_HEADER -#include #include /// \defgroup id Ids @@ -21,11 +21,13 @@ IKARUS_BEGIN_HEADER /// \brief A wrapper around a 64 bit integer that represents the id of an object. /// \details They are stored as 64 bit integers with the following layout: -/// - first 8 bits: #IkarusObjectType - 255 possible values, 0 for special values +/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. +/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. +/// - next 7 bits: #IkarusObjectType - 127 possible values, 0 for special values /// - last 56 bits: incremented counter generated by the database struct IkarusId { /// \private \brief The value of the id. - uint64_t value; + int64_t data; }; /// \brief A special id returned by failed functions. @@ -34,9 +36,16 @@ IkarusId const IKARUS_ID_NONE{0}; IkarusId const IKARUS_ID_UNSPECIFIED{1}; /// \private \brief Generates a new id for the given object type. -/// \param object_type The type of the object to generate an id for. +/// \param data The data from which the id will be constructed. /// \return The generated id. -IkarusId ikarus_id_from_integer(IkarusObjectType object_type); +/// \pre data must be valid under the format described in Id. It should also point to an object in the database. +IkarusId ikarus_id_from_data(int64_t data); + +/// \brief Checkes whether two ids are equal +/// \param left the left side of the comparison +/// \param right the right side of the comparison +/// \return True if the bits from the left id are equal to the bits of the right id +bool ikarus_id_is_equal(IkarusId left, IkarusId right); /// \brief Fetches the object type of the given id. /// \param id The id to fetch the object type for. diff --git a/include/ikarus/types/object_type.h b/include/ikarus/types/object_type.h index ca3dfb0..1c0cf9d 100644 --- a/include/ikarus/types/object_type.h +++ b/include/ikarus/types/object_type.h @@ -25,37 +25,37 @@ enum IkarusFolderType { /// \remark folders have the first bit set and then mirror the object type of the underlying object enum IkarusObjectType { /// \brief Not an object or no object. - ObjectType_None = 0, + IkarusObjectType_None = 0, /// \brief An IkarusBlueprint. - ObjectType_Blueprint = 0b0000'0001, + IkarusObjectType_Blueprint = 0b0000'0001, /// \brief An IkarusProperty. - ObjectType_Property = 0b0000'0010, + IkarusObjectType_Property = 0b0000'0010, /// \brief An IkarusEntity. - ObjectType_Entity = 0b0000'0011, + IkarusObjectType_Entity = 0b0000'0011, /// \brief An IkarusBlueprintFolder - ObjectType_BlueprintFolder = 0b1000'0001, + IkarusObjectType_BlueprintFolder = 0b1000'0001, /// \brief An IkarusPropertyFolder - ObjectType_PropertyFolder = 0b1000'0010, + IkarusObjectType_PropertyFolder = 0b1000'0010, /// \brief An IkarusEntityFolder - ObjectType_EntityFolder = 0b1000'0011, + IkarusObjectType_EntityFolder = 0b1000'0011, }; /// \brief A bitset of IkarusObjectType%s. enum ObjectTypes { /// \brief No object type. - ObjectTypes_None = 0, + IkarusObjectTypes_None = 0, /// \brief An IkarusBlueprint. - ObjectTypes_Blueprint = 1 << ObjectType_Blueprint, + IkarusObjectTypes_Blueprint = 1 << IkarusObjectType_Blueprint, /// \brief An IkarusProperty. - ObjectTypes_Property = 1 << ObjectType_Property, + IkarusObjectTypes_Property = 1 << IkarusObjectType_Property, /// \brief An IkarusEntity. - ObjectTypes_Entity = 1 << ObjectType_Entity, + IkarusObjectTypes_Entity = 1 << IkarusObjectType_Entity, /// \brief An IkarusBlueprintFolder - ObjectTypes_BlueprintFolder = 1 << ObjectType_BlueprintFolder, + IkarusObjectTypes_BlueprintFolder = 1 << IkarusObjectType_BlueprintFolder, /// \brief An IkarusPropertyFolder - ObjectTypes_PropertyFolder = 1 << ObjectType_PropertyFolder, + IkarusObjectTypes_PropertyFolder = 1 << IkarusObjectType_PropertyFolder, /// \brief An IkarusEntityFolder - ObjectTypes_EntityFolder = 1 << ObjectType_EntityFolder, + IkarusObjectTypes_EntityFolder = 1 << IkarusObjectType_EntityFolder, }; // @} diff --git a/src/types/id.cpp b/src/types/id.cpp new file mode 100644 index 0000000..53f2689 --- /dev/null +++ b/src/types/id.cpp @@ -0,0 +1,56 @@ +#include "ikarus/types/id.h" + +#include + +IkarusId ikarus_id_from_data(int64_t data) { + return IkarusId { + .data = data + }; +} + +IkarusObjectType ikarus_id_get_object_type(IkarusId id) { + return static_cast(id.data >> 56); +} + +bool ikarus_id_is_equal(IkarusId left, IkarusId right) { + return left.data == right.data; +} + +bool ikarus_id_is_none(IkarusId id) { + return ikarus_id_is_equal(id, IKARUS_ID_NONE); +} + +bool ikarus_id_is_unspecified(IkarusId id) { + return ikarus_id_is_equal(id, IKARUS_ID_UNSPECIFIED); +} + +TEST_CASE("id_object_type", "[id]") { + auto id = ikarus_id_from_data(static_cast(IkarusObjectType_Blueprint) << 56); + + REQUIRE(ikarus_id_get_object_type(id) == IkarusObjectType_Blueprint); + REQUIRE(!ikarus_id_is_none(id) == IkarusObjectType_Blueprint); + REQUIRE(!ikarus_id_is_unspecified(id) == IkarusObjectType_Blueprint); +} + +TEST_CASE("id_equal", "[id]") { + auto id = ikarus_id_from_data(static_cast(IkarusObjectType_Blueprint) << 56); + auto copy = id; + auto third = ikarus_id_from_data(static_cast(IkarusObjectType_Property) << 56); + + REQUIRE(ikarus_id_is_equal(id, copy)); + REQUIRE(!ikarus_id_is_equal(id, third)); +} + +TEST_CASE("id_none", "[id]") { + auto id = IKARUS_ID_NONE; + + REQUIRE(ikarus_id_is_none(id)); + REQUIRE(!ikarus_id_is_unspecified(id)); +} + +TEST_CASE("id_unspecified", "[id]") { + auto id = IKARUS_ID_UNSPECIFIED; + + REQUIRE(!ikarus_id_is_none(id)); + REQUIRE(ikarus_id_is_unspecified(id)); +} From 085d99aa1afef5d6352f37c3bd5b4e8785aa1a2c Mon Sep 17 00:00:00 2001 From: Folling Date: Fri, 25 Aug 2023 08:52:34 +0200 Subject: [PATCH 087/166] object type interface Signed-off-by: Folling --- include/ikarus/types/object_type.h | 36 +++++++++++++++++++----------- src/types/object_type.cpp | 32 ++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 src/types/object_type.cpp diff --git a/include/ikarus/types/object_type.h b/include/ikarus/types/object_type.h index 1c0cf9d..fd8685a 100644 --- a/include/ikarus/types/object_type.h +++ b/include/ikarus/types/object_type.h @@ -9,39 +9,40 @@ IKARUS_BEGIN_HEADER /// @{ /// \brief The type of a folder. -/// \remark folders have the first bit set and then mirror the object type of the underlying object +/// \remark These values are identical to the associated values of IkarusObjectType. enum IkarusFolderType { /// \brief Not a folder or no folder. IkarusFolderType_None = 0, /// \brief An IkarusBlueprintFolder - IkarusFolderType_BlueprintFolder = 0b1000'0001, + IkarusFolderType_BlueprintFolder = 17, /// \brief An IkarusPropertyFolder - IkarusFolderType_PropertyFolder = 0b1000'0010, + IkarusFolderType_PropertyFolder = 18, /// \brief An IkarusEntityFolder - IkarusFolderType_EntityFolder = 0b1000'0011, + IkarusFolderType_EntityFolder = 19, }; /// \brief The type of an object. -/// \remark folders have the first bit set and then mirror the object type of the underlying object +/// \remark Folders have the 4th bit set. enum IkarusObjectType { /// \brief Not an object or no object. IkarusObjectType_None = 0, /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 0b0000'0001, + IkarusObjectType_Blueprint = 1, /// \brief An IkarusProperty. - IkarusObjectType_Property = 0b0000'0010, + IkarusObjectType_Property = 2, /// \brief An IkarusEntity. - IkarusObjectType_Entity = 0b0000'0011, + IkarusObjectType_Entity = 3, /// \brief An IkarusBlueprintFolder - IkarusObjectType_BlueprintFolder = 0b1000'0001, + IkarusObjectType_BlueprintFolder = IkarusFolderType_BlueprintFolder, /// \brief An IkarusPropertyFolder - IkarusObjectType_PropertyFolder = 0b1000'0010, + IkarusObjectType_PropertyFolder = IkarusFolderType_PropertyFolder, /// \brief An IkarusEntityFolder - IkarusObjectType_EntityFolder = 0b1000'0011, + IkarusObjectType_EntityFolder = IkarusFolderType_EntityFolder, }; +// because of the nature of bitsets, the largest possible object-type is 31 /// \brief A bitset of IkarusObjectType%s. -enum ObjectTypes { +enum IkarusObjectTypes { /// \brief No object type. IkarusObjectTypes_None = 0, /// \brief An IkarusBlueprint. @@ -58,6 +59,15 @@ enum ObjectTypes { IkarusObjectTypes_EntityFolder = 1 << IkarusObjectType_EntityFolder, }; +/// \brief Converts an IkarusFolderType to an IkarusObjectType. +/// \param type The IkarusFolderType to convert. +/// \return The converted IkarusObjectType, representing the folder type. +IKA_API IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type); +/// \brief Converts an IkarusObjectType to a bitset of IkarusObjectTypes. +/// \param type The IkarusObjectType to convert. +/// \return The converted IkarusObjectTypes, representing the object type. +IKA_API IkarusObjectTypes ikarus_object_type_to_bitset(IkarusObjectType type); + // @} -IKARUS_END_HEADER \ No newline at end of file +IKARUS_END_HEADER diff --git a/src/types/object_type.cpp b/src/types/object_type.cpp new file mode 100644 index 0000000..7f5e377 --- /dev/null +++ b/src/types/object_type.cpp @@ -0,0 +1,32 @@ +#include + +#include + +IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type) { + return static_cast(type); +} + +IkarusObjectTypes ikarus_object_type_to_bitset(IkarusObjectType type) { + if (type == 0) { + return static_cast(0); + } + + return static_cast(1 << type); +} + +TEST_CASE("folder_to_object_type_conversion", "[object_type]") { + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_None) == IkarusObjectType_None); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_BlueprintFolder) == IkarusObjectType_BlueprintFolder); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_PropertyFolder) == IkarusObjectType_PropertyFolder); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_EntityFolder) == IkarusObjectType_EntityFolder); +} + +TEST_CASE("object_type_to_bitset_conversion", "[object_type]") { + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_None) == IkarusObjectTypes_None); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Blueprint) == IkarusObjectTypes_Blueprint); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Property) == IkarusObjectTypes_Property); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Entity) == IkarusObjectTypes_Entity); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_BlueprintFolder) == IkarusObjectTypes_BlueprintFolder); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_PropertyFolder) == IkarusObjectTypes_PropertyFolder); + REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_EntityFolder) == IkarusObjectTypes_EntityFolder); +} From 6a2821f7c0c47c83d82ff3b38ed3a0c339cea73c Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 29 Aug 2023 14:12:08 +0200 Subject: [PATCH 088/166] restructure into smaller files & add IWYU/clang-tidy Signed-off-by: Folling --- .clang-format | 51 +----- CMakeLists.txt | 54 ++++-- include/ikarus/folders/blueprint_folder.h | 22 +++ include/ikarus/folders/entity_folder.h | 22 +++ include/ikarus/folders/folder.h | 76 ++++++++ include/ikarus/folders/folder_type.h | 26 +++ include/ikarus/folders/property_folder.h | 23 +++ include/ikarus/{types => }/id.h | 5 +- include/ikarus/objects/blueprint.h | 24 +++ include/ikarus/objects/entity.h | 41 +++++ include/ikarus/objects/object.h | 123 +++++++++++++ include/ikarus/objects/object_type.h | 38 ++++ include/ikarus/objects/property.h | 73 ++++++++ .../ikarus/{types => objects}/property_type.h | 2 +- include/ikarus/scopes/blueprint_scope.h | 26 +++ include/ikarus/scopes/entity_scope.h | 26 +++ include/ikarus/scopes/object_scope.h | 84 +++++++++ include/ikarus/scopes/property_scope.h | 65 +++++++ include/ikarus/types/object.h | 164 ------------------ include/ikarus/types/object_scope.h | 149 ---------------- include/ikarus/types/object_type.h | 73 -------- include/ikarus/{types => values}/value.h | 5 +- src/{types => }/id.cpp | 8 +- src/objects/object.cpp | 27 +++ src/objects/object_type.cpp | 16 ++ src/{types => scopes}/object_scope.cpp | 130 +++++++------- src/types/object_type.cpp | 32 ---- src/{types => values}/value.cpp | 16 +- 28 files changed, 845 insertions(+), 556 deletions(-) create mode 100644 include/ikarus/folders/blueprint_folder.h create mode 100644 include/ikarus/folders/entity_folder.h create mode 100644 include/ikarus/folders/folder.h create mode 100644 include/ikarus/folders/folder_type.h create mode 100644 include/ikarus/folders/property_folder.h rename include/ikarus/{types => }/id.h (97%) create mode 100644 include/ikarus/objects/blueprint.h create mode 100644 include/ikarus/objects/entity.h create mode 100644 include/ikarus/objects/object.h create mode 100644 include/ikarus/objects/object_type.h create mode 100644 include/ikarus/objects/property.h rename include/ikarus/{types => objects}/property_type.h (96%) create mode 100644 include/ikarus/scopes/blueprint_scope.h create mode 100644 include/ikarus/scopes/entity_scope.h create mode 100644 include/ikarus/scopes/object_scope.h create mode 100644 include/ikarus/scopes/property_scope.h delete mode 100644 include/ikarus/types/object.h delete mode 100644 include/ikarus/types/object_scope.h delete mode 100644 include/ikarus/types/object_type.h rename include/ikarus/{types => values}/value.h (98%) rename src/{types => }/id.cpp (93%) create mode 100644 src/objects/object.cpp create mode 100644 src/objects/object_type.cpp rename src/{types => scopes}/object_scope.cpp (62%) delete mode 100644 src/types/object_type.cpp rename src/{types => values}/value.cpp (96%) diff --git a/.clang-format b/.clang-format index 0372fa7..513423e 100644 --- a/.clang-format +++ b/.clang-format @@ -81,72 +81,31 @@ FixNamespaceComments: false IncludeBlocks: Regroup IncludeCategories: - # Relative Includes - # "blubb.h" / "blubb/blubber.h" - Regex: '^".+\.(h|hpp)"$' Priority: 1 - - # C Includes - # - Regex: '^<[a-z0-9_]+\.h>$' Priority: 2 - - # C++ Includes - # - Regex: '^<[a-z0-9_]+>$' Priority: 3 - - # expected - # - Regex: '^$' Priority: 4 - - # libfmt - # - Regex: '^$' Priority: 5 - - # nlohmann::json - # - Regex: '^$' Priority: 6 - - # ranges - # - Regex: '^$' Priority: 7 - - # ICU - # - - Regex: '^$' + - Regex: '^$' Priority: 8 - - # ranges - # - - Regex: '^$' + - Regex: '^$' Priority: 9 - - # ranges - # - - Regex: '^$' + - Regex: '^$' Priority: 10 - - # ranges - # - - Regex: '^$' + - Regex: '^$' Priority: 11 - - # ranges - # - - Regex: '^$' + - Regex: '^$' Priority: 12 - # ranges - # ranges - # - - Regex: '^$' - Priority: 13 - IndentAccessModifiers: false IndentCaseBlocks: false IndentCaseLabels: false diff --git a/CMakeLists.txt b/CMakeLists.txt index a7faf39..5edd8b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,9 @@ cmake_minimum_required(VERSION 3.18) project(ikarus) +option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF) +option(LIBIKARUS_ENABLE_LINTS "Enable linting" OFF) + set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 20) @@ -11,7 +14,7 @@ add_subdirectory(include) add_subdirectory(src) add_library( - libikarus OBJECT + libikarus SHARED ${INCLUDE_FILES} ${SOURCE_FILES} ) @@ -27,7 +30,7 @@ target_include_directories( ) target_link_libraries( - libikarus PUBLIC + libikarus PRIVATE Catch2::Catch2WithMain ) @@ -37,19 +40,40 @@ target_link_libraries( sqlitecpp ) -set_target_properties( - libikarus PROPERTIES - LINKER_LANGUAGE CXX -) +if (LIBIKARUS_ENABLE_LINTS) + find_program(IWYU_PATH NAMES include-what-you-use iwyu REQUIRED) + find_program(CLANG_TIDY_PATH NAMES clang-tidy REQUIRED) -add_executable(ikarus_tests ${SOURCE_FILES}) -target_link_libraries(ikarus_tests PRIVATE Catch2::Catch2WithMain) + set_property(TARGET libikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) + string( + CONCAT CLANG_TIDY_OPTIONS + "-checks=-*," + "bugprone-*," + "concurrency-*," + "cppcoreguidelines-*,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-type-union-access," + "misc-*," + "modernize-*,-modernize-use-trailing-return-type," + "performance-*," + "portability-*," + "readability-*,-readability-identifier-length,-readability-magic-numbers,-readability-function-cognitive-complexity" + ) -target_include_directories( - ikarus_tests PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/include -) + set_property( + TARGET libikarus + PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY_PATH};${CLANG_TIDY_OPTIONS} + ) +endif () -include(CTest) -include(vendor/catch2/extras/Catch.cmake) -catch_discover_tests(ikarus_tests) +if (LIBIKARUS_ENABLE_TESTS) + add_executable(libikarus_tests ${SOURCE_FILES}) + target_link_libraries(libikarus_tests PRIVATE Catch2::Catch2WithMain) + + target_include_directories( + libikarus_tests PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/include + ) + + include(CTest) + include(vendor/catch2/extras/Catch.cmake) + catch_discover_tests(libikarus_tests) +endif () \ No newline at end of file diff --git a/include/ikarus/folders/blueprint_folder.h b/include/ikarus/folders/blueprint_folder.h new file mode 100644 index 0000000..8ab973e --- /dev/null +++ b/include/ikarus/folders/blueprint_folder.h @@ -0,0 +1,22 @@ +#pragma once + +/// \file blueprint_folder.h +/// \author Folling + +#include +#include + +/// \addtogroup folder Folders +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A blueprint folder, storing blueprints and other blueprint folders. +struct IkarusBlueprintFolder { + /// \private \brief The id of the blueprint folder. + IkarusId id; +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/folders/entity_folder.h b/include/ikarus/folders/entity_folder.h new file mode 100644 index 0000000..2bffa3f --- /dev/null +++ b/include/ikarus/folders/entity_folder.h @@ -0,0 +1,22 @@ +#pragma once + +/// \file entity_folder.h +/// \author Folling + +#include +#include + +/// \addtogroup folder Folders +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A entity folder, storing entities and other entity folders. +struct IkarusEntityFolder { + /// \private \brief The id of the entity folder. + IkarusId id; +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/folders/folder.h b/include/ikarus/folders/folder.h new file mode 100644 index 0000000..0cdf946 --- /dev/null +++ b/include/ikarus/folders/folder.h @@ -0,0 +1,76 @@ +#pragma once + +/// \file folder.h +/// \author Folling + +#include +#include +#include +#include +#include + +IKARUS_BEGIN_HEADER + +/// \defgroup folder Folders +/// \brief Folders are used to group objects together. +/// @{ + +/// \private \brief The data of a folder. +union IkarusFolderData { + /// \private \brief The blueprint folder data of the folder. + IkarusBlueprintFolder blueprint_folder; + /// \private \brief The property folder data of the folder. + IkarusPropertyFolder property_folder; + /// \private \brief The entity folder data of the folder. + IkarusEntityFolder entity_folder; +}; + +/// \brief A generic folder. Similar to how Objects wrap all types of objects, Folders wrap all types of folders. +struct IkarusFolder { + /// \private \brief The data of the folder. + IkarusFolderData data; + + /// \private \brief The type of the folder. + IkarusFolderType type; +}; + +/// \brief Constructs a folder from a blueprint folder. +/// \param blueprint_folder The blueprint folder to construct the folder from. +/// \return The constructed folder. +IKA_API IkarusFolder ikarus_folder_from_blueprint_folder(IkarusBlueprintFolder blueprint_folder); +/// \brief Constructs a folder from a property folder. +/// \param property_folder The property folder to construct the folder from. +/// \return The constructed folder. +IKA_API IkarusFolder ikarus_folder_from_property_folder(IkarusPropertyFolder property_folder); +/// \brief Constructs a folder from an entity folder. +/// \param entity_folder The entity folder to construct the folder from. +/// \return The constructed folder. +IKA_API IkarusFolder ikarus_folder_from_entity_folder(IkarusEntityFolder entity_folder); + +/// \brief Fetches the folder type of a folder. +/// \param folder The folder to fetch the type of. +/// \return The type of the folder. +IKA_API IkarusFolderType ikarus_folder_get_type(IkarusFolder folder); + +/// \brief Checks if two folders are equal. +/// \details Since ids store the type of the object, this boils down to a simple comparison of the ids. +/// \param left The left side of the comparison. +/// \param right The right side of the comparison. +/// \return True if the folders are equal, false otherwise +IKA_API bool ikarus_folder_is_equal(IkarusFolder left, IkarusFolder right); + +/// \brief Visits a folder. Calling the appropriate function for the folder's type. +/// \param folder The folder to visit. +/// \param blueprint The function to call if the folder is a blueprint folder. +/// \param property The function to call if the folder is a property folder. +/// \param entity The function to call if the folder is an entity folder. +/// \param data The data to pass to the functions. +IKA_API void ikarus_folder_visit( + IkarusFolder folder, + void (*blueprint)(IkarusBlueprintFolder, void *), + void (*property)(IkarusPropertyFolder, void *), + void (*entity)(IkarusEntityFolder, void *), + void * data +); + +IKARUS_END_HEADER diff --git a/include/ikarus/folders/folder_type.h b/include/ikarus/folders/folder_type.h new file mode 100644 index 0000000..2a63e46 --- /dev/null +++ b/include/ikarus/folders/folder_type.h @@ -0,0 +1,26 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_FOLDER_TYPES + +/// \file folder_type.h +/// \author Folling + +#include + +/// \addtogroup folder folders +/// @{ + +/// \brief The type of an folder. +/// \remark Folders have the 8th bit set. +enum IkarusFolderType { + /// \brief Not a folder or no folder. + IkarusFolderType_None = 0, + /// \brief An IkarusBlueprintFolder + IkarusFolderType_BlueprintFolder = 0b1000'0001, + /// \brief An IkarusPropertyFolder + IkarusFolderType_PropertyFolder = 0b1000'0010, + /// \brief An IkarusEntityFolder + IkarusFolderType_EntityFolder = 0b1000'0011, +}; + +/// @} diff --git a/include/ikarus/folders/property_folder.h b/include/ikarus/folders/property_folder.h new file mode 100644 index 0000000..8e6709f --- /dev/null +++ b/include/ikarus/folders/property_folder.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +/// \file property_folder.h +/// \author Folling + +/// \addtogroup folder Folders +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A property folder, storing properties and other property folders. +/// \remark Property folders are scoped to the blueprint or entity they are associated with. +struct IkarusPropertyFolder { + /// \private \brief The id of the property folder. + IkarusId id; +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/types/id.h b/include/ikarus/id.h similarity index 97% rename from include/ikarus/types/id.h rename to include/ikarus/id.h index e2ca0b5..d184e63 100644 --- a/include/ikarus/types/id.h +++ b/include/ikarus/id.h @@ -6,12 +6,11 @@ #pragma once #include +#include #include IKARUS_BEGIN_HEADER -#include - /// \defgroup id Ids /// \brief Ids are used to identify objects in the database. /// \details They are stored as 64 bit integers with the following layout: @@ -64,4 +63,4 @@ IKA_API bool ikarus_id_is_unspecified(IkarusId id); /// @} -IKARUS_END_HEADER \ No newline at end of file +IKARUS_END_HEADER diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h new file mode 100644 index 0000000..bd0abd3 --- /dev/null +++ b/include/ikarus/objects/blueprint.h @@ -0,0 +1,24 @@ +#pragma once + +/// \file blueprint.h +/// \author Folling + +#include +#include + +/// \defgroup blueprints Blueprints +/// \brief Blueprints are templates for entities. + +IKARUS_BEGIN_HEADER + +/// \brief A blueprint object. +/// \details A blueprint is a collection of properties which can be linked to entities. +/// Each entity the blueprint is linked to will have values for the blueprints properties. +struct IkarusBlueprint { + /// \private \brief The id of the blueprint. + IkarusId id; +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h new file mode 100644 index 0000000..980c03e --- /dev/null +++ b/include/ikarus/objects/entity.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +/// \file entity.h +/// \author Folling + +/// \defgroup entities Entities +/// \brief Entities are the core building blocks of Ikarus. + +IKARUS_BEGIN_HEADER + +/// \brief Entities are the core building blocks of Ikarus. +/// \detials Blueprints and Properties define the structure of the data. +/// Entities define the data itself. +/// +/// Properties can be associated with Entities in two ways: +/// - Directly: The property is linked to the entity. +/// - Indirectly: The property is linked to a blueprint of the entity. +/// +/// For each property an entity is linked to, it has a value. These values depend on the property's type. +/// For more information on the types see the property documentation. +/// +/// Values are the core type of data within Ikarus. +/// Each value is associated with one page and one property. +/// +/// \remark Values are typed, the type of a value is specified by its associated property. +/// For more information on the types see the property documentation. +/// +/// \remark Values are guaranteed to be in valid format for a given type +/// but not guaranteed to be valid under the settings of the property. +/// This is because changing the settings can invalidate existing values without resetting them. +struct IkarusEntity { + /// \private \brief The ID of the entity. + IkarusId id; +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h new file mode 100644 index 0000000..ef8c592 --- /dev/null +++ b/include/ikarus/objects/object.h @@ -0,0 +1,123 @@ +#pragma once + +/// \file object.h +/// \author Folling + +#include +#include +#include +#include +#include +#include +#include +#include + +/// \defgroup object Objects +/// \brief Objects are a compound type of all types of objects in the database. +/// \details The following objects currently exist: +/// - blueprints +/// - properties +/// - entities +/// - blueprint folders +/// - property folders +/// - entity folders +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusFolder; + +/// \private \brief The data of an object. +union IkarusObjectData { + /// \private \brief The blueprint data of the object. + IkarusBlueprint blueprint; + /// \private \brief The property data of the object. + IkarusProperty property; + /// \private \brief The entity data of the object. + IkarusEntity entity; + /// \private \brief The blueprint folder data of the object. + IkarusBlueprintFolder blueprint_folder; + /// \private \brief The property folder data of the object. + IkarusPropertyFolder property_folder; + /// \private \brief The entity folder data of the object. + IkarusEntityFolder entity_folder; +}; + +/// \brief A generic object. Wraps all types of objects, including folders. +struct IkarusObject { + /// \private \brief The type of the object. + IkarusObjectType type; + /// \private \brief The data of the object. + IkarusObjectData data; +}; + +/// \brief Constructs an object from a blueprint. +/// \param blueprint The blueprint to construct the object from. +/// \return The constructed object, representing the blueprint. +IKA_API IkarusObject ikarus_object_from_blueprint(IkarusBlueprint blueprint); + +/// \brief Constructs an object from a property. +/// \param property The property to construct the object from. +/// \return The constructed object, representing the property. +IKA_API IkarusObject ikarus_object_from_property(IkarusProperty property); + +/// \brief Constructs an object from an entity. +/// \param entity The entity to construct the object from. +/// \return The constructed object, representing the entity. +IKA_API IkarusObject ikarus_object_from_entity(IkarusEntity entity); + +/// \brief Constructs an object from a blueprint folder. +/// \param blueprint The folder to construct the object from. +/// \return The constructed object, representing the folder. +IKA_API IkarusObject ikarus_object_from_blueprint_folder(IkarusBlueprintFolder folder); + +/// \brief Constructs an object from a property folder. +/// \param property The folder to construct the object from. +/// \return The constructed object, representing the folder. +IKA_API IkarusObject ikarus_object_from_property_folder(IkarusPropertyFolder folder); + +/// \brief Constructs an object from a entity folder. +/// \param entity The folder to construct the object from. +/// \return The constructed object, representing the folder. +IKA_API IkarusObject ikarus_object_from_entity_folder(IkarusEntityFolder folder); + +/// \brief Constructs an object from a folder. +/// \param folder The folder to construct the object from. +/// \return The constructed object, representing the folder. +IKA_API IkarusObject ikarus_object_from_folder(IkarusFolder folder); + +/// \brief Compares two objects for equality. +/// \details Since ids store the type of the object, this boils down to a simple comparison of the ids. +/// \param left The left side of the comparison. +/// \param right The right side of the comparison. +/// \return Whether the objects are equal. +IKA_API bool ikarus_object_is_equal(IkarusObject left, IkarusObject right); + +/// \brief Fetches the type of an object. +/// \param object The object to fetch the type of. +/// \return The type of the object. +IKA_API IkarusObjectType ikarus_object_get_type(IkarusObject object); + +/// \brief Visits an object. Calling the appropriate function for the object's type. +/// \param object The object to visit. +/// \param blueprint The function to call if the object is a blueprint. +/// \param property The function to call if the object is a property. +/// \param entity The function to call if the object is an entity. +/// \param blueprint_folder The function to call if the object is a blueprint folder. +/// \param property_folder The function to call if the object is a property folder. +/// \param entity_folder The function to call if the object is an entity folder. +/// \param data The data to pass to the functions. +IKA_API void ikarus_object_visit( + IkarusObject object, + void (*visit_blueprint)(IkarusBlueprint, void *), + void (*visit_property)(IkarusProperty, void *), + void (*visit_entity)(IkarusEntity, void *), + void (*visit_blueprint_folder)(IkarusBlueprintFolder, void *), + void (*visit_property_folder)(IkarusPropertyFolder, void *), + void (*visit_entity_folder)(IkarusEntityFolder, void *), + void * data +); + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h new file mode 100644 index 0000000..0fafc68 --- /dev/null +++ b/include/ikarus/objects/object_type.h @@ -0,0 +1,38 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_OBJECT_TYPES + +/// \file object.h +/// \author Folling + +#include +#include + +/// \addtogroup object Objects +/// @{ + +/// \brief The type of an object. +/// \remark Folders have the 4th bit set. +enum IkarusObjectType { + /// \brief Not an object or no object. + IkarusObjectType_None = 0, + /// \brief An IkarusBlueprint. + IkarusObjectType_Blueprint = 1, + /// \brief An IkarusProperty. + IkarusObjectType_Property = 2, + /// \brief An IkarusEntity. + IkarusObjectType_Entity = 3, + /// \brief An IkarusBlueprintFolder + IkarusObjectType_BlueprintFolder = IkarusFolderType_BlueprintFolder, + /// \brief An IkarusPropertyFolder + IkarusObjectType_PropertyFolder = IkarusFolderType_PropertyFolder, + /// \brief An IkarusEntityFolder + IkarusObjectType_EntityFolder = IkarusFolderType_EntityFolder, +}; + +/// \brief Constructs an IkarusObjectType from an IkarusFolderType. +/// \param type The IkarusFolderType of which to construct the IkarusObjectType from. +/// \return The constructed IkarusObjectType, representing the folder type. +IKA_API IkarusObjectType ikarus_object_type_from_folder_type(IkarusFolderType type); + +/// @} diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h new file mode 100644 index 0000000..d104ac3 --- /dev/null +++ b/include/ikarus/objects/property.h @@ -0,0 +1,73 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION, IMPLEMENTATION_DETAIL_PROPERTY_TYPES + +/// \file property.h +/// \author Folling + +#include +#include +#include +#include + +/// \defgroup properties Properties +/// \brief Properties define the structure and types of data. + +IKARUS_BEGIN_HEADER + +/// \brief Properties are the placeholders of values for entities. +/// \details Each entity can have any number of properties. +/// Every property has a type that identifies the kind of data that can be put in. +/// +/// 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) +/// +/// Every property has settings which can be used to customise the property further. +/// Two settings that are shared among all properties are the following: +/// - Multiple +/// - Allow undefined +/// +/// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. +/// The latter allows you to specify an "unknown" value for a property. +/// It might not be known if a character is dead or not for example. +/// +/// Each entity associated with the property has a value for it. +/// +/// Properties can also be added to blueprints in which case they are available for all entities associated with the +/// blueprint. +/// +/// We call properties within entities "Entity Properties" and properties within blueprints "Blueprint Properties". +/// +/// \remark Properties are scoped to the blueprint or entity they are associated with. +/// \remark Values for properties are lazily created as space saving measure. +/// Fetching the value for some property of some entity will return the property's default value if none is specified. +/// This default value is specified when the property is created and can be updated later. +/// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. +struct IkarusProperty { + /// \private \brief The id of the property. + IkarusId id; +}; + +/// \brief The type of a property. +/// \details Designates the type of data stored by the property as well as which settings are +/// available. +/// \see IkarusPropertySettings +enum IkarusPropertyType { + /// \brief A true/false boolean-like value. + IkarusPropertyType_Toggle, + /// \brief An arbitrary numeric value. + IkarusPropertyType_Number, + /// \brief An arbitrary textual value. + IkarusPropertyType_Text, +}; + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/types/property_type.h b/include/ikarus/objects/property_type.h similarity index 96% rename from include/ikarus/types/property_type.h rename to include/ikarus/objects/property_type.h index d22e1a6..3709a37 100644 --- a/include/ikarus/types/property_type.h +++ b/include/ikarus/objects/property_type.h @@ -2,7 +2,7 @@ // IMPLEMENTATION_DETAIL_PROPERTY_TYPES -/// \file id.h +/// \file property_type.h /// \author Folling #include diff --git a/include/ikarus/scopes/blueprint_scope.h b/include/ikarus/scopes/blueprint_scope.h new file mode 100644 index 0000000..eac5eac --- /dev/null +++ b/include/ikarus/scopes/blueprint_scope.h @@ -0,0 +1,26 @@ +#pragma once + +/// \file blueprint_scope.h +/// \author Folling + +#include +#include + +/// \addtogroup object_scopes ObjectScopes +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief The global scope of all blueprints. +struct IkarusBlueprintScope { + /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. + short _dummy; +}; + +/// \brief Creates a blueprint scope. +/// \return The created blueprint scope. +IKA_API IkarusBlueprintScope ikarus_blueprint_scope_create(); + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/scopes/entity_scope.h b/include/ikarus/scopes/entity_scope.h new file mode 100644 index 0000000..e29d725 --- /dev/null +++ b/include/ikarus/scopes/entity_scope.h @@ -0,0 +1,26 @@ +#pragma once + +/// \file entity_scope.h +/// \author Folling + +#include +#include + +/// \addtogroup object_scopes ObjectScopes +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief The global scope of all entities. +struct IkarusEntityScope { + /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. + short _dummy; +}; + +/// \brief Creates a entity scope. +/// \return The created entity scope. +IKA_API IkarusEntityScope ikarus_entity_scope_create(); + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/scopes/object_scope.h b/include/ikarus/scopes/object_scope.h new file mode 100644 index 0000000..1c16d9f --- /dev/null +++ b/include/ikarus/scopes/object_scope.h @@ -0,0 +1,84 @@ +#pragma once + +// IMPLEMENTATION_DETAIL_OBJECT_SCOPES + +/// \file object_scope.h +/// \author Folling + +#include +#include +#include +#include + +/// \defgroup object_scopes Object Scopes +/// \brief Scopes define where objects belong to. +/// \details They are required to differentiate between different types of objects with NULL as their parent. +/// @{ + +IKARUS_BEGIN_HEADER + +/// \private \brief The data for an object scope. +union IkarusObjectScopeData { + /// \private \brief The blueprint data of the scope. + IkarusBlueprintScope _blueprint; + /// \private \brief The property data of the scope. + IkarusPropertyScope _property; + /// \private \brief The entity data of the scope. + IkarusEntityScope _entity; +}; + +/// The type of an object scope. +enum IkarusObjectScopeType { + /// \brief The scope is a blueprint scope. + IkarusObjectScopeType_Blueprint, + /// \brief The scope is a property scope. + IkarusObjectScopeType_Property, + /// \brief The scope is an entity scope. + IkarusObjectScopeType_Entity +}; + +/// \brief The scope of an object. +struct IkarusObjectScope { + /// \private \brief Represents the type of the scope. + IkarusObjectScopeType _type; + /// \private \brief Represents the data of the scope. + IkarusObjectScopeData _data; +}; + +/// \brief Converts a blueprint scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope scope); + +/// \brief Converts a property scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope scope); + +/// Converts an entity scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +IKA_API IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope scope); + +/// \brief Fetches the type of an object scope. +/// \param scope The scope to fetch the type of. +/// \return The type of the scope. +IKA_API IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope scope); + +/// \brief Visits an object scope, calling the appropriate function. +/// \param scope The scope to visit. +/// \param blueprint The function to call if the scope is an #IkarusBlueprintScope. +/// \param property The function to call if the scope is an #IkarusPropertyScope. +/// \param entity The function to call if the scope is an #IkarusEntityScope. +/// \remark function pointers may be null in which case they are not called. +IKA_API void ikarus_object_scope_visit( + IkarusObjectScope scope, + void (*blueprint)(IkarusBlueprintScope, void *), + void (*property)(IkarusPropertyScope, void *), + void (*entity)(IkarusEntityScope, void *), + void * data +); + +/// @} + +IKARUS_END_HEADER diff --git a/include/ikarus/scopes/property_scope.h b/include/ikarus/scopes/property_scope.h new file mode 100644 index 0000000..6de365c --- /dev/null +++ b/include/ikarus/scopes/property_scope.h @@ -0,0 +1,65 @@ +#pragma once + +/// \file property_scope.h +/// \author Folling + +#include +#include +#include +#include + +/// \addtogroup object_scopes ObjectScopes +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief Data for a property scope. This can either be a blueprint or an entity. +union IkarusPropertyScopeData { + /// \private \brief The blueprint the property is scoped to. + IkarusBlueprint _blueprint; + /// \private \brief The entity the property is scoped to. + IkarusEntity _entity; +}; + +/// \brief The type of a property scope. This can either be a blueprint or an entity. +enum IkarusPropertyScopeType { + /// \brief The property is scoped to a blueprint. + IkarusPropertyScopeType_Blueprint, + /// \brief The property is scoped to an entity. + IkarusPropertyScopeType_Entity +}; + +/// \brief The scope of a property +struct IkarusPropertyScope { + /// \private \brief Represents the type of the scope. + IkarusPropertyScopeType _type; + /// \private \brief Represents the data of the scope. + IkarusPropertyScopeData _data; +}; + +/// \brief Creates a property scope from a blueprint. +/// \param blueprint The blueprint the property is scoped to. +/// \return The created property scope. +IKA_API IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint blueprint); +/// \brief Creates a property scope from a entity. +/// \param entity The entity the property is scoped to. +/// \return The created property scope. +IKA_API IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity entity); + +/// \brief Fetches the type of an property scope. +/// \param scope The scope to fetch the type of. +/// \return The type of the scope. +IKA_API IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope scope); + +/// \brief Visits a property scope, calling the appropriate function. +/// \param scope The scope to to visit +/// \param blueprint The function to call if the property is scoped to a blueprint. +/// \param entity The function to call if the property is scoped to an entity. +/// \param data Optional data to pass to the functions. +void ikarus_property_scope_visit( + IkarusPropertyScope scope, void (*blueprint)(IkarusBlueprint, void *), void (*entity)(IkarusEntity, void *), void * data +); + +IKARUS_END_HEADER + +// @} diff --git a/include/ikarus/types/object.h b/include/ikarus/types/object.h deleted file mode 100644 index 9d8b9bd..0000000 --- a/include/ikarus/types/object.h +++ /dev/null @@ -1,164 +0,0 @@ -#pragma once - -// IMPLEMENTATION_DETAIL_OBJECT_TYPES -// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION -// IMPLEMENTATION_DETAIL_PROPERTY_TYPES - -/// \file id.h -/// \author Folling - -/// \defgroup object Objects -/// \brief Objects are a compound type of all types of objects in the database. -/// \details The following objects currently exist: -/// - blueprints -/// - properties -/// - entities -/// - blueprint folders -/// - property folders -/// - entity folders -/// @{ - -#include - -IKARUS_BEGIN_HEADER - -/// \brief A blueprint object. -/// \details A blueprint is a collection of properties which can be linked to entities. -/// Each entity the blueprint is linked to will have values for the blueprints properties. -struct IkarusBlueprint { - IkarusId id; -}; - -/// \brief Properties are the placeholders of values for entities. -/// \details Each entity can have any number of properties. -/// Every property has a type that identifies the kind of data that can be put in. -/// -/// 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) -/// -/// Every property has settings which can be used to customise the property further. -/// Two settings that are shared among all properties are the following: -/// - Multiple -/// - Allow undefined -/// -/// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. -/// The latter allows you to specify an "unknown" value for a property. -/// It might not be known if a character is dead or not for example. -/// -/// Each entity associated with the property has a value for it. -/// -/// Properties can also be added to blueprints in which case they are available for all entities associated with the -/// blueprint. -/// -/// We call properties within entities "Entity Properties" and properties within blueprints "Blueprint Properties". -/// -/// \remark Values for properties are lazily created as space saving measure. -/// Fetching the value for some property of some entity will return the property's default value if none is specified. -/// This default value is specified when the property is created and can be updated later. -/// -/// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. -struct IkarusProperty { - /// \private \brief The ID of the property. - IkarusId id; -}; - -/// \brief Entities are the core building blocks of Ikarus. -/// \detials Blueprints and Properties define the structure of the data. -/// Entities define the data itself. -/// -/// Properties can be associated with Entities in two ways: -/// - Directly: The property is linked to the entity. -/// - Indirectly: The property is linked to a blueprint of the entity. -/// -/// For each property an entity is linked to, it has a value. These values depend on the property's type. -/// For more information on the types see the property documentation. -/// -/// Values are the core type of data within Ikarus. -/// Each value is associated with one page and one property. -/// -/// \remark Values are typed, the type of a value is specified by its associated property. -/// For more information on the types see the property documentation. -/// -/// \remark Values are guaranteed to be in valid format for a given type -/// but not guaranteed to be valid under the settings of the property. -/// This is because changing the settings can invalidate existing values without resetting them. -struct IkarusEntity { - /// \private \brief The ID of the entity. - IkarusId id; -}; - -/// \brief A blueprint folder. -/// \see Folder -struct IkarusBlueprintFolder { - /// \private \brief The ID of the folder. - IkarusId id; -}; - -/// \brief A property folder. -/// \remark Property folders are scoped to the blueprint or entity they are associated with. -/// \see Folder -struct IkarusPropertyFolder { - /// \private \brief The ID of the folder. - IkarusId id; -}; - -/// \brief An entity folder. -/// \see Folder -struct IkarusEntityFolder { - /// \private \brief The ID of the folder. - IkarusId id; -}; - -/// \private \brief The data of a folder. -union IkarusFolderData { - /// \private \brief The blueprint folder data of the folder. - IkarusBlueprintFolder blueprint_folder; - /// \private \brief The property folder data of the folder. - IkarusPropertyFolder property_folder; - /// \private \brief The entity folder data of the folder. - IkarusEntityFolder entity_folder; -}; - -/// \brief A generic folder. Similar to how Objects wrap all types of objects, Folders wrap all types of folders. -struct IkarusFolder { - /// \private \brief The data of the folder. - IkarusFolderData data; - - /// \private \brief The type of the folder. - IkarusFolderType type; -}; - -/// \private \brief The data of an object. -union IkarusObjectData { - /// \private \brief The blueprint data of the object. - IkarusBlueprint blueprint; - /// \private \brief The property data of the object. - IkarusProperty property; - /// \private \brief The entity data of the object. - IkarusEntity entity; - /// \private \brief The blueprint folder data of the object. - IkarusBlueprintFolder blueprint_folder; - /// \private \brief The property folder data of the object. - IkarusPropertyFolder property_folder; - /// \private \brief The entity folder data of the object. - IkarusEntityFolder entity_folder; -}; - -/// \brief A generic object. Wraps all types of objects, including folders. -struct IkarusObject { - /// \private \brief The data of the object. - IkarusObjectData data; - /// \private \brief The type of the object. - IkarusObjectType type; -}; - -// @} - -IKARUS_END_HEADER diff --git a/include/ikarus/types/object_scope.h b/include/ikarus/types/object_scope.h deleted file mode 100644 index 1fe51e1..0000000 --- a/include/ikarus/types/object_scope.h +++ /dev/null @@ -1,149 +0,0 @@ -// IMPLEMENTATION_DETAIL_OBJECT_SCOPES, IMPLEMENTATION_DETAIL_TREE_LAYOUT - -/// \file object_scope.h -/// \author Folling - -/// \defgroup object_scopes Object Scopes -/// \brief Scopes define where objects belong to. -/// \details They are required to differentiate between different types of objects with NULL as their parent. -/// @{ - -#pragma once - -#include -#include - -IKARUS_BEGIN_HEADER - -/// \brief The global scope of all blueprints. -struct IkarusBlueprintScope { - /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. - short _dummy; -}; - -/// \brief Data for a property scope. This can either be a blueprint or an entity. -union IkarusPropertyScopeData { - /// \private \brief The blueprint the property is scoped to. - IkarusBlueprint _blueprint; - /// \private \brief The entity the property is scoped to. - IkarusEntity _entity; -}; - -/// \brief The type of a property scope. This can either be a blueprint or an entity. -enum IkarusPropertyScopeType { - /// \brief The property is scoped to a blueprint. - IkarusPropertyScopeType_Blueprint, - /// \brief The property is scoped to an entity. - IkarusPropertyScopeType_Entity -}; - -/// \brief The scope of a property -struct IkarusPropertyScope { - /// \private \brief Represents the type of the scope. - IkarusPropertyScopeType _type; - /// \private \brief Represents the data of the scope. - IkarusPropertyScopeData _data; -}; - -/// The global scope of all entities. -struct IkarusEntityScope { - /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. - short _dummy; -}; - -/// \private \brief The data for an object scope. -union IkarusObjectScopeData { - /// \private \brief The blueprint data of the scope. - IkarusBlueprintScope _blueprint; - /// \private \brief The property data of the scope. - IkarusPropertyScope _property; - /// \private \brief The entity data of the scope. - IkarusEntityScope _entity; -}; - -/// The type of an object scope. -enum IkarusObjectScopeType { - /// \brief The scope is a blueprint scope. - IkarusObjectScopeType_Blueprint, - /// \brief The scope is a property scope. - IkarusObjectScopeType_Property, - /// \brief The scope is an entity scope. - IkarusObjectScopeType_Entity -}; - -/// \brief The scope of an object. -struct IkarusObjectScope { - /// \private \brief Represents the type of the scope. - IkarusObjectScopeType _type; - /// \private \brief Represents the data of the scope. - IkarusObjectScopeData _data; -}; - -/// \brief Creates a blueprint scope. -/// \return The created blueprint scope. -IKA_API IkarusBlueprintScope ikarus_blueprint_scope_create(); -/// \brief Converts a blueprint scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope); - -/// \brief Creates a property scope from a blueprint. -/// \param blueprint The blueprint the property is scoped to. -/// \return The created property scope. -IKA_API IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint const * blueprint); -/// \brief Creates a property scope from a entity. -/// \param entity The entity the property is scoped to. -/// \return The created property scope. -IKA_API IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity const * entity); -/// \brief Converts a property scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope); - -/// \brief Fetches the type of an property scope. -/// \param scope The scope to fetch the type of. -/// \return The type of the scope. -IKA_API IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope const * scope); - -/// \brief Visits a property scope, calling the appropriate function. -/// \param scope The scope to to visit -/// \param blueprint The function to call if the property is scoped to a blueprint. -/// \param entity The function to call if the property is scoped to an entity. -/// \param data Optional data to pass to the functions. -void ikarus_property_scope_visit( - IkarusPropertyScope const * scope, - void (*blueprint)(IkarusBlueprint const *, void *), - void (*entity)(IkarusEntity const *, void *), - void * data -); - -/// \brief Creates an entity scope. -/// \return The created entity scope. -IKA_API IkarusEntityScope ikarus_entity_scope_create(); -/// Converts an entity scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope); - -/// \brief Fetches the type of an object scope. -/// \param scope The scope to fetch the type of. -/// \return The type of the scope. -IKA_API IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope const * scope); - -/// \brief Visits an object scope, calling the appropriate function. -/// \param scope The scope to visit. -/// \param blueprint The function to call if the scope is an #IkarusBlueprintScope. -/// \param property The function to call if the scope is an #IkarusPropertyScope. -/// \param entity The function to call if the scope is an #IkarusEntityScope. -/// \remark function pointers may be null in which case they are not called. -IKA_API void ikarus_object_scope_visit( - IkarusObjectScope const * scope, - void (*blueprint)(IkarusBlueprintScope const *, void *), - void (*property)(IkarusPropertyScope const *, void *), - void (*entity)(IkarusEntityScope const *, void *), - void * data -); - -/// @} - -IKARUS_END_HEADER diff --git a/include/ikarus/types/object_type.h b/include/ikarus/types/object_type.h deleted file mode 100644 index fd8685a..0000000 --- a/include/ikarus/types/object_type.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include - -IKARUS_BEGIN_HEADER - -/// \defgroup object_types ObjectTypes -/// \brief ObjectTypes are used to identify the type of objects. -/// @{ - -/// \brief The type of a folder. -/// \remark These values are identical to the associated values of IkarusObjectType. -enum IkarusFolderType { - /// \brief Not a folder or no folder. - IkarusFolderType_None = 0, - /// \brief An IkarusBlueprintFolder - IkarusFolderType_BlueprintFolder = 17, - /// \brief An IkarusPropertyFolder - IkarusFolderType_PropertyFolder = 18, - /// \brief An IkarusEntityFolder - IkarusFolderType_EntityFolder = 19, -}; - -/// \brief The type of an object. -/// \remark Folders have the 4th bit set. -enum IkarusObjectType { - /// \brief Not an object or no object. - IkarusObjectType_None = 0, - /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 1, - /// \brief An IkarusProperty. - IkarusObjectType_Property = 2, - /// \brief An IkarusEntity. - IkarusObjectType_Entity = 3, - /// \brief An IkarusBlueprintFolder - IkarusObjectType_BlueprintFolder = IkarusFolderType_BlueprintFolder, - /// \brief An IkarusPropertyFolder - IkarusObjectType_PropertyFolder = IkarusFolderType_PropertyFolder, - /// \brief An IkarusEntityFolder - IkarusObjectType_EntityFolder = IkarusFolderType_EntityFolder, -}; - -// because of the nature of bitsets, the largest possible object-type is 31 -/// \brief A bitset of IkarusObjectType%s. -enum IkarusObjectTypes { - /// \brief No object type. - IkarusObjectTypes_None = 0, - /// \brief An IkarusBlueprint. - IkarusObjectTypes_Blueprint = 1 << IkarusObjectType_Blueprint, - /// \brief An IkarusProperty. - IkarusObjectTypes_Property = 1 << IkarusObjectType_Property, - /// \brief An IkarusEntity. - IkarusObjectTypes_Entity = 1 << IkarusObjectType_Entity, - /// \brief An IkarusBlueprintFolder - IkarusObjectTypes_BlueprintFolder = 1 << IkarusObjectType_BlueprintFolder, - /// \brief An IkarusPropertyFolder - IkarusObjectTypes_PropertyFolder = 1 << IkarusObjectType_PropertyFolder, - /// \brief An IkarusEntityFolder - IkarusObjectTypes_EntityFolder = 1 << IkarusObjectType_EntityFolder, -}; - -/// \brief Converts an IkarusFolderType to an IkarusObjectType. -/// \param type The IkarusFolderType to convert. -/// \return The converted IkarusObjectType, representing the folder type. -IKA_API IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type); -/// \brief Converts an IkarusObjectType to a bitset of IkarusObjectTypes. -/// \param type The IkarusObjectType to convert. -/// \return The converted IkarusObjectTypes, representing the object type. -IKA_API IkarusObjectTypes ikarus_object_type_to_bitset(IkarusObjectType type); - -// @} - -IKARUS_END_HEADER diff --git a/include/ikarus/types/value.h b/include/ikarus/values/value.h similarity index 98% rename from include/ikarus/types/value.h rename to include/ikarus/values/value.h index d636d27..e1457af 100644 --- a/include/ikarus/types/value.h +++ b/include/ikarus/values/value.h @@ -2,9 +2,12 @@ // IMPLEMENTATION_DETAIL_PROPERTY_TYPES +/// \file value.h +/// \author Folling + #include +#include #include -#include IKARUS_BEGIN_HEADER diff --git a/src/types/id.cpp b/src/id.cpp similarity index 93% rename from src/types/id.cpp rename to src/id.cpp index 53f2689..caa08be 100644 --- a/src/types/id.cpp +++ b/src/id.cpp @@ -1,11 +1,11 @@ -#include "ikarus/types/id.h" +#include "ikarus/id.h" #include +#include + IkarusId ikarus_id_from_data(int64_t data) { - return IkarusId { - .data = data - }; + return IkarusId{.data = data}; } IkarusObjectType ikarus_id_get_object_type(IkarusId id) { diff --git a/src/objects/object.cpp b/src/objects/object.cpp new file mode 100644 index 0000000..7378c39 --- /dev/null +++ b/src/objects/object.cpp @@ -0,0 +1,27 @@ +#include "ikarus/objects/object.h" + +#include + +#include + +#include + +IkarusObjectType ikarus_object_get_type(IkarusObject object) { + return object.type; +} + +TEST_CASE("object_type", "[object]") { + auto types = { + IkarusObjectType_Blueprint, + IkarusObjectType_Property, + IkarusObjectType_Entity, + IkarusObjectType_BlueprintFolder, + IkarusObjectType_PropertyFolder, + IkarusObjectType_EntityFolder, + }; + + for (auto type : types) { + auto object = IkarusObject{.type = type}; + REQUIRE(ikarus_object_get_type(object) == type); + } +} diff --git a/src/objects/object_type.cpp b/src/objects/object_type.cpp new file mode 100644 index 0000000..a9a3e32 --- /dev/null +++ b/src/objects/object_type.cpp @@ -0,0 +1,16 @@ +#include "ikarus/objects/object_type.h" + +#include + +#include + +IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type) { + return static_cast(type); +} + +TEST_CASE("folder_to_object_type_conversion", "[object_type]") { + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_None) == IkarusObjectType_None); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_BlueprintFolder) == IkarusObjectType_BlueprintFolder); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_PropertyFolder) == IkarusObjectType_PropertyFolder); + REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_EntityFolder) == IkarusObjectType_EntityFolder); +} diff --git a/src/types/object_scope.cpp b/src/scopes/object_scope.cpp similarity index 62% rename from src/types/object_scope.cpp rename to src/scopes/object_scope.cpp index a4f2b49..348b007 100644 --- a/src/types/object_scope.cpp +++ b/src/scopes/object_scope.cpp @@ -1,52 +1,56 @@ -#include "ikarus/types/object_scope.h" +#include "ikarus/scopes/object_scope.h" +#include #include #include +#include +#include +#include +#include +#include + IkarusBlueprintScope ikarus_blueprint_scope_create() { return IkarusBlueprintScope{._dummy = 0}; } -IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope) { +IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope scope) { IkarusObjectScopeData data{}; - data._blueprint = *scope; + data._blueprint = scope; return IkarusObjectScope{._type = IkarusObjectScopeType_Blueprint, ._data = data}; } -IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint const * blueprint) { +IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint blueprint) { IkarusPropertyScopeData data{}; - data._blueprint = *blueprint; + data._blueprint = blueprint; return IkarusPropertyScope{._type = IkarusPropertyScopeType_Blueprint, ._data = data}; } -IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity const * entity) { +IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity entity) { IkarusPropertyScopeData data{}; - data._entity = *entity; + data._entity = entity; return IkarusPropertyScope{._type = IkarusPropertyScopeType_Entity, ._data = data}; } -IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope) { +IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope scope) { IkarusObjectScopeData data{}; - data._property = *scope; + data._property = scope; return IkarusObjectScope{._type = IkarusObjectScopeType_Property, ._data = data}; } -IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope const * scope) { - return scope->_type; +IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope scope) { + return scope._type; } void ikarus_property_scope_visit( - IkarusPropertyScope const * scope, - void (*blueprint)(IkarusBlueprint const *, void *), - void (*entity)(IkarusEntity const *, void *), - void * data + IkarusPropertyScope scope, void(blueprint)(IkarusBlueprint, void *), void(entity)(IkarusEntity, void *), void * data ) { - switch (scope->_type) { - case IkarusPropertyScopeType_Blueprint: blueprint(&scope->_data._blueprint, data); break; - case IkarusPropertyScopeType_Entity: entity(&scope->_data._entity, data); break; + switch (scope._type) { + case IkarusPropertyScopeType_Blueprint: blueprint(scope._data._blueprint, data); break; + case IkarusPropertyScopeType_Entity: entity(scope._data._entity, data); break; } } @@ -54,40 +58,40 @@ IkarusEntityScope ikarus_entity_scope_create() { return IkarusEntityScope{._dummy = 0}; } -IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope) { +IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope scope) { IkarusObjectScopeData data{}; - data._entity = *scope; + data._entity = scope; return IkarusObjectScope{._type = IkarusObjectScopeType_Entity, ._data = data}; } -IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope const * scope) { - return scope->_type; +IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope scope) { + return scope._type; } void ikarus_object_scope_visit( - IkarusObjectScope const * scope, - void (*blueprint)(IkarusBlueprintScope const *, void *), - void (*property)(IkarusPropertyScope const *, void *), - void (*entity)(IkarusEntityScope const *, void *), + IkarusObjectScope scope, + void(blueprint)(IkarusBlueprintScope, void *), + void(property)(IkarusPropertyScope, void *), + void(entity)(IkarusEntityScope, void *), void * data ) { - switch (scope->_type) { + switch (scope._type) { case IkarusObjectScopeType_Blueprint: { if (blueprint != nullptr) { - blueprint(&scope->_data._blueprint, data); + blueprint(scope._data._blueprint, data); } break; } case IkarusObjectScopeType_Property: { if (property != nullptr) { - property(&scope->_data._property, data); + property(scope._data._property, data); } break; } case IkarusObjectScopeType_Entity: { if (entity != nullptr) { - entity(&scope->_data._entity, data); + entity(scope._data._entity, data); } break; } @@ -96,7 +100,7 @@ void ikarus_object_scope_visit( TEST_CASE("blueprint_object_scope_conversion", "[object_scope]") { auto blueprint_scope = ikarus_blueprint_scope_create(); - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); REQUIRE(blueprint_object_scope._type == IkarusObjectScopeType_Blueprint); } @@ -104,24 +108,24 @@ TEST_CASE("property_scope_type", "[object_scope]") { auto blueprint = IkarusBlueprint{}; auto entity = IkarusEntity{}; - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); - auto property_entity_scope = ikarus_property_scope_create_entity(&entity); + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); + auto property_entity_scope = ikarus_property_scope_create_entity(entity); - REQUIRE(ikarus_property_scope_get_type(&property_blueprint_scope) == IkarusPropertyScopeType_Blueprint); - REQUIRE(ikarus_property_scope_get_type(&property_entity_scope) == IkarusPropertyScopeType_Entity); + REQUIRE(ikarus_property_scope_get_type(property_blueprint_scope) == IkarusPropertyScopeType_Blueprint); + REQUIRE(ikarus_property_scope_get_type(property_entity_scope) == IkarusPropertyScopeType_Entity); } TEST_CASE("property_object_scope_conversion", "[object_scope]") { auto blueprint = IkarusBlueprint{}; auto entity = IkarusEntity{}; - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); - auto property_blueprint_object_scope = ikarus_property_scope_to_object_scope(&property_blueprint_scope); + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); + auto property_blueprint_object_scope = ikarus_property_scope_to_object_scope(property_blueprint_scope); REQUIRE(property_blueprint_object_scope._type == IkarusObjectScopeType_Property); - auto property_entity_scope = ikarus_property_scope_create_entity(&entity); - auto property_entity_object_scope = ikarus_property_scope_to_object_scope(&property_entity_scope); + auto property_entity_scope = ikarus_property_scope_create_entity(entity); + auto property_entity_object_scope = ikarus_property_scope_to_object_scope(property_entity_scope); REQUIRE(property_entity_object_scope._type == IkarusObjectScopeType_Property); } @@ -130,24 +134,24 @@ TEST_CASE("property_scope_visiting", "[object_scope]") { auto blueprint = IkarusBlueprint{}; auto entity = IkarusEntity{}; - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(&blueprint); - auto property_entity_scope = ikarus_property_scope_create_entity(&entity); + auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); + auto property_entity_scope = ikarus_property_scope_create_entity(entity); int test = 0; ikarus_property_scope_visit( - &property_blueprint_scope, - [](IkarusBlueprint const * _, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusEntity const * _, void * data) { *reinterpret_cast(data) = 2; }, + property_blueprint_scope, + [](IkarusBlueprint, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusEntity, void * data) { *reinterpret_cast(data) = 2; }, &test ); REQUIRE(test == 1); ikarus_property_scope_visit( - &property_entity_scope, - [](IkarusBlueprint const * _, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusEntity const * _, void * data) { *reinterpret_cast(data) = 2; }, + property_entity_scope, + [](IkarusBlueprint, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusEntity, void * data) { *reinterpret_cast(data) = 2; }, &test ); @@ -156,7 +160,7 @@ TEST_CASE("property_scope_visiting", "[object_scope]") { TEST_CASE("entity_object_scope_conversion", "[object_scope]") { auto entity_scope = ikarus_entity_scope_create(); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); REQUIRE(entity_object_scope._type == IkarusObjectScopeType_Entity); } @@ -164,28 +168,28 @@ TEST_CASE("object_scope_type_fetching", "[object_scope]") { auto blueprint = IkarusBlueprint{}; auto blueprint_scope = ikarus_blueprint_scope_create(); - auto property_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto property_scope = ikarus_property_scope_create_blueprint(blueprint); auto entity_scope = ikarus_entity_scope_create(); - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); - auto property_object_scope = ikarus_property_scope_to_object_scope(&property_scope); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); + auto property_object_scope = ikarus_property_scope_to_object_scope(property_scope); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); - REQUIRE(ikarus_object_scope_get_type(&blueprint_object_scope) == IkarusObjectScopeType_Blueprint); - REQUIRE(ikarus_object_scope_get_type(&property_object_scope) == IkarusObjectScopeType_Property); - REQUIRE(ikarus_object_scope_get_type(&entity_object_scope) == IkarusObjectScopeType_Entity); + REQUIRE(ikarus_object_scope_get_type(blueprint_object_scope) == IkarusObjectScopeType_Blueprint); + REQUIRE(ikarus_object_scope_get_type(property_object_scope) == IkarusObjectScopeType_Property); + REQUIRE(ikarus_object_scope_get_type(entity_object_scope) == IkarusObjectScopeType_Entity); } TEST_CASE("object_scope_visiting", "[object_scope]") { auto blueprint = IkarusBlueprint{}; auto blueprint_scope = ikarus_blueprint_scope_create(); - auto property_scope = ikarus_property_scope_create_blueprint(&blueprint); + auto property_scope = ikarus_property_scope_create_blueprint(blueprint); auto entity_scope = ikarus_entity_scope_create(); - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(&blueprint_scope); - auto property_object_scope = ikarus_property_scope_to_object_scope(&property_scope); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(&entity_scope); + auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); + auto property_object_scope = ikarus_property_scope_to_object_scope(property_scope); + auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); auto scopes = { std::make_pair(blueprint_object_scope, 1), @@ -197,10 +201,10 @@ TEST_CASE("object_scope_visiting", "[object_scope]") { int test = 0; ikarus_object_scope_visit( - &scope, - [](IkarusBlueprintScope const * _, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusPropertyScope const * _, void * data) { *reinterpret_cast(data) = 2; }, - [](IkarusEntityScope const * _, void * data) { *reinterpret_cast(data) = 3; }, + scope, + [](IkarusBlueprintScope, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusPropertyScope, void * data) { *reinterpret_cast(data) = 2; }, + [](IkarusEntityScope, void * data) { *reinterpret_cast(data) = 3; }, &test ); diff --git a/src/types/object_type.cpp b/src/types/object_type.cpp deleted file mode 100644 index 7f5e377..0000000 --- a/src/types/object_type.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include - -#include - -IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type) { - return static_cast(type); -} - -IkarusObjectTypes ikarus_object_type_to_bitset(IkarusObjectType type) { - if (type == 0) { - return static_cast(0); - } - - return static_cast(1 << type); -} - -TEST_CASE("folder_to_object_type_conversion", "[object_type]") { - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_None) == IkarusObjectType_None); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_BlueprintFolder) == IkarusObjectType_BlueprintFolder); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_PropertyFolder) == IkarusObjectType_PropertyFolder); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_EntityFolder) == IkarusObjectType_EntityFolder); -} - -TEST_CASE("object_type_to_bitset_conversion", "[object_type]") { - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_None) == IkarusObjectTypes_None); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Blueprint) == IkarusObjectTypes_Blueprint); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Property) == IkarusObjectTypes_Property); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_Entity) == IkarusObjectTypes_Entity); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_BlueprintFolder) == IkarusObjectTypes_BlueprintFolder); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_PropertyFolder) == IkarusObjectTypes_PropertyFolder); - REQUIRE(ikarus_object_type_to_bitset(IkarusObjectType_EntityFolder) == IkarusObjectTypes_EntityFolder); -} diff --git a/src/types/value.cpp b/src/values/value.cpp similarity index 96% rename from src/types/value.cpp rename to src/values/value.cpp index 251db2c..56fa9c5 100644 --- a/src/types/value.cpp +++ b/src/values/value.cpp @@ -1,10 +1,16 @@ -#include "ikarus/types/value.h" +#include "ikarus/values/value.h" #include +#include +#include #include +#include #include +#include +#include + /// \brief Creates an indeterminate entity value of a given type. /// \param type The type of the value. /// \return The entity value. @@ -39,7 +45,7 @@ IkarusEntityValue ikarus_value_create_number(long double value) { IkarusEntityValue ikarus_value_create_text(char const * value) { if (value == nullptr) { return value_create_invalid(IkarusPropertyType_Text); - }; + } return IkarusEntityValue{ ._type = IkarusPropertyType_Text, @@ -326,9 +332,9 @@ TEST_CASE("visit_value", "[value]") { ikarus_value_visit( &value, - [](IkarusToggleValue const * _, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusNumberValue const * _, void * data) { *reinterpret_cast(data) = 2; }, - [](IkarusTextValue const * _, void * data) { *reinterpret_cast(data) = 3; }, + [](IkarusToggleValue const *, void * data) { *reinterpret_cast(data) = 1; }, + [](IkarusNumberValue const *, void * data) { *reinterpret_cast(data) = 2; }, + [](IkarusTextValue const *, void * data) { *reinterpret_cast(data) = 3; }, &test ); From ca67866d74cdbb81a91f99604e41c5260b25e870 Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 1 Nov 2023 18:03:03 +0100 Subject: [PATCH 089/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index afe7b16..416e326 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit afe7b165002ccf86a37da5b6b157ce4ff9db0401 +Subproject commit 416e326c60f3763a43dec8c66e58616ab50d1a2a From 67711a8d3976f368c527c71cbcd8f0cfd02fdc4e Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 6 Nov 2023 13:14:39 +0100 Subject: [PATCH 090/166] finalise interface & documentation Signed-off-by: Folling --- .clang-tidy | 26 +++ CMakeLists.txt | 49 ++-- LICENSE.md | 22 +- docs/DoxyFile | 7 +- docs/enum_format_fix.js | 11 - docs/format_fix.js | 68 ++++++ docs/header.html | 3 +- include/ikarus/folders/blueprint_folder.h | 143 +++++++++++- include/ikarus/folders/blueprint_tree_item.h | 31 +++ include/ikarus/folders/entity_folder.h | 143 +++++++++++- include/ikarus/folders/entity_tree_item.h | 31 +++ include/ikarus/folders/folder.h | 62 +---- include/ikarus/folders/folder_type.h | 10 +- include/ikarus/folders/property_folder.h | 156 ++++++++++++- include/ikarus/folders/property_tree_item.h | 31 +++ include/ikarus/global.h | 16 ++ include/ikarus/id.h | 66 ------ include/ikarus/objects/blueprint.h | 201 ++++++++++++++++- include/ikarus/objects/entity.h | 226 ++++++++++++++++++- include/ikarus/objects/object.h | 109 ++------- include/ikarus/objects/object_type.h | 38 ++-- include/ikarus/objects/property.h | 199 ++++++++++++++-- include/ikarus/objects/property_source.h | 47 ++++ include/ikarus/objects/property_type.h | 24 +- include/ikarus/objects/property_type_info.h | 79 +++++++ include/ikarus/project/project.h | 179 +++++++++++++++ include/ikarus/scopes/blueprint_scope.h | 16 +- include/ikarus/scopes/entity_scope.h | 19 +- include/ikarus/scopes/object_scope.h | 66 ++---- include/ikarus/scopes/property_scope.h | 57 ++--- include/ikarus/stdtypes.h | 1 + include/ikarus/values/value.h | 182 ++++++--------- src/CMakeLists.txt | 1 + src/folders/blueprint_folder.hpp | 8 + src/folders/entity_folder.hpp | 8 + src/folders/folder.hpp | 13 ++ src/folders/property_folder.hpp | 8 + src/id.cpp | 26 +-- src/id.hpp | 40 ++++ src/objects/blueprint.hpp | 8 + src/objects/entity.hpp | 8 + src/objects/object.cpp | 10 +- src/objects/object.hpp | 15 ++ src/objects/object_type.cpp | 16 -- src/objects/property.hpp | 8 + src/projects/project.hpp | 10 + src/scopes/blueprint_scope.cpp | 12 + src/scopes/blueprint_scope.hpp | 5 + src/scopes/entity_scope.cpp | 12 + src/scopes/entity_scope.hpp | 5 + src/scopes/object_scope.cpp | 205 ++++------------- src/scopes/object_scope.hpp | 11 + src/scopes/property_scope.cpp | 35 +++ src/scopes/property_scope.hpp | 12 + src/values/value.cpp | 58 ++--- vendor/sqlitecpp | 2 +- 56 files changed, 2074 insertions(+), 780 deletions(-) create mode 100644 .clang-tidy delete mode 100644 docs/enum_format_fix.js create mode 100644 docs/format_fix.js create mode 100644 include/ikarus/folders/blueprint_tree_item.h create mode 100644 include/ikarus/folders/entity_tree_item.h create mode 100644 include/ikarus/folders/property_tree_item.h create mode 100644 include/ikarus/global.h delete mode 100644 include/ikarus/id.h create mode 100644 include/ikarus/objects/property_source.h create mode 100644 include/ikarus/objects/property_type_info.h create mode 100644 include/ikarus/project/project.h create mode 100644 src/folders/blueprint_folder.hpp create mode 100644 src/folders/entity_folder.hpp create mode 100644 src/folders/folder.hpp create mode 100644 src/folders/property_folder.hpp create mode 100644 src/id.hpp create mode 100644 src/objects/blueprint.hpp create mode 100644 src/objects/entity.hpp create mode 100644 src/objects/object.hpp delete mode 100644 src/objects/object_type.cpp create mode 100644 src/objects/property.hpp create mode 100644 src/projects/project.hpp create mode 100644 src/scopes/blueprint_scope.cpp create mode 100644 src/scopes/blueprint_scope.hpp create mode 100644 src/scopes/entity_scope.cpp create mode 100644 src/scopes/entity_scope.hpp create mode 100644 src/scopes/object_scope.hpp create mode 100644 src/scopes/property_scope.cpp create mode 100644 src/scopes/property_scope.hpp diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..fa1f43f --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,26 @@ +Checks: >- + -*, + bugprone-*, + cppcoreguidelines-*, -cppcoreguidelines-owning-memory, + clang-analyzer-*, + google-*, -google-readability-todo, + modernize-*, -modernize-use-trailing-return-type, + performance-*, -performance-enum-size, + portability-*, + readability-*, -readability-redundant-access-specifiers +CheckOptions: + readability-identifier-length.IgnoredParameterNames: '^(db|rc|id)$' + readability-identifier-length.IgnoredLoopCounterNames: '^[ij]$' + readability-identifier-length.IgnoredVariableNames: '^(db|rc|id)$' + cppcoreguidelines-avoid-do-while.IgnoreMacros: Yes +HeaderFileExtensions: + - h + - hpp + - tpp + - ipp +ImplementationFileExtensions: + - c + - cpp +FormatStyle: file +InheritParentConfig: false +WarningsAsErrors: '*' diff --git a/CMakeLists.txt b/CMakeLists.txt index 5edd8b6..1bbb826 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,9 +3,10 @@ project(ikarus) option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF) option(LIBIKARUS_ENABLE_LINTS "Enable linting" OFF) +option(LIBIKARUS_BUILD_DOCS "Build documentation" OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) @@ -29,11 +30,6 @@ target_include_directories( ${CMAKE_CURRENT_LIST_DIR}/src ) -target_link_libraries( - libikarus PRIVATE - Catch2::Catch2WithMain -) - target_link_libraries( libikarus PRIVATE cppbase @@ -45,35 +41,44 @@ if (LIBIKARUS_ENABLE_LINTS) find_program(CLANG_TIDY_PATH NAMES clang-tidy REQUIRED) set_property(TARGET libikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) - string( - CONCAT CLANG_TIDY_OPTIONS - "-checks=-*," - "bugprone-*," - "concurrency-*," - "cppcoreguidelines-*,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-type-union-access," - "misc-*," - "modernize-*,-modernize-use-trailing-return-type," - "performance-*," - "portability-*," - "readability-*,-readability-identifier-length,-readability-magic-numbers,-readability-function-cognitive-complexity" - ) - set_property( TARGET libikarus - PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY_PATH};${CLANG_TIDY_OPTIONS} + PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY_PATH}; ) endif () if (LIBIKARUS_ENABLE_TESTS) add_executable(libikarus_tests ${SOURCE_FILES}) - target_link_libraries(libikarus_tests PRIVATE Catch2::Catch2WithMain) + target_link_libraries(libikarus_tests PRIVATE sqlitecpp Catch2::Catch2WithMain) target_include_directories( - libikarus_tests PUBLIC + libikarus_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include + ${CMAKE_CURRENT_LIST_DIR}/src ) include(CTest) include(vendor/catch2/extras/Catch.cmake) catch_discover_tests(libikarus_tests) +endif () + +if (LIBIKARUS_BUILD_DOCS) + find_program(DOXYGEN_PATH NAMES doxygen REQUIRED) + add_custom_target( + libikarus_docs + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/docs + COMMAND ${DOXYGEN_PATH} DoxyFile + COMMENT "Generating documentation with Doxygen" + VERBATIM + ) + + add_dependencies( + libikarus + libikarus_docs + ) + + add_dependencies( + libikarus_tests + libikarus_docs + ) endif () \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 598a8d4..5f2c6f6 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,10 +1,20 @@ -Copyright 2019-2023 Folling (mail@folling.io) +Copyright 2019-2023 Folling (folling@ikarus.world) -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following + disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/docs/DoxyFile b/docs/DoxyFile index 61f3460..1c5a659 100644 --- a/docs/DoxyFile +++ b/docs/DoxyFile @@ -6,11 +6,14 @@ FULL_SIDEBAR = NO GENERATE_LATEX = NO GENERATE_TREEVIEW = YES HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5 -HTML_EXTRA_FILES = ../vendor/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js ../vendor/doxygen-awesome-css/doxygen-awesome-fragment-copy-button.js ./enum_format_fix.js +HTML_EXTRA_FILES = ../vendor/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js ../vendor/doxygen-awesome-css/doxygen-awesome-fragment-copy-button.js ./format_fix.js HTML_EXTRA_STYLESHEET = ../vendor/doxygen-awesome-css/doxygen-awesome.css HTML_HEADER = header.html INPUT = .. OUTPUT_DIRECTORY = generated PROJECT_BRIEF = A C-API implementation for Ikarus, a tool for worldbuilding. PROJECT_NAME = LIBIKARUS -RECURSIVE = YES \ No newline at end of file +RECURSIVE = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBER_DOCS = NO +EXTRACT_PRIVATE = NO diff --git a/docs/enum_format_fix.js b/docs/enum_format_fix.js deleted file mode 100644 index 2507ab8..0000000 --- a/docs/enum_format_fix.js +++ /dev/null @@ -1,11 +0,0 @@ -// maximum efficiency -function enumFormatFix() { - Array.from(document.getElementsByClassName("memItemRight")).forEach((elem) => { - elem.innerHTML = elem.innerHTML.replaceAll("
", ""); - elem.innerHTML = elem.innerHTML.replaceAll(" ", ""); - elem.innerHTML = elem.innerHTML.replaceAll("{", "{
    "); - elem.innerHTML = elem.innerHTML.replaceAll("\n,", ","); - elem.innerHTML = elem.innerHTML.replaceAll(",", ",
    "); - elem.innerHTML = elem.innerHTML.replaceAll("}", "
}"); - }); -} \ No newline at end of file diff --git a/docs/format_fix.js b/docs/format_fix.js new file mode 100644 index 0000000..d7db696 --- /dev/null +++ b/docs/format_fix.js @@ -0,0 +1,68 @@ +// maximum efficiency +function enumFormatFix() { + Array.from(document.getElementsByClassName("memItemRight")).forEach((elem) => { + if (elem.innerHTML.includes("{")) { + let str = elem.innerHTML; + + str = str.replaceAll("
", ""); + str = str.replaceAll(" ", ""); + str = str.replaceAll("{", "{
    "); + str = str.replaceAll("\n,", ","); + str = str.replaceAll(",", ",
    "); + str = str.replaceAll("}", "
}"); + + elem.innerHTML = str + } + }); +} + +function paramFormatFix() { + Array.from(document.getElementsByClassName("memItemRight")).forEach((elem) => { + if (elem.innerHTML.includes("(")) { + let str = elem.innerHTML; + + let res = ""; + let bracket_level = 0; + let hit_comma = false; + + for (let c of str) { + let new_hit_comma = false; + + if (c === '(') { + if (bracket_level === 0) { + res += "(
    "; + } else { + res += '('; + } + + bracket_level++; + } else if (c === ')') { + if (bracket_level === 1) { + res += "
)"; + } else { + res += ')'; + } + + bracket_level--; + } else if (c === ',') { + if (bracket_level === 1) { + res += ",
    "; + } else { + res += ','; + } + + new_hit_comma = true; + } else if (c === ' ' && hit_comma && bracket_level === 1) { + new_hit_comma = false; + // skip this space + } else { + res += c; + } + + hit_comma = new_hit_comma; + } + + elem.innerHTML = res + } + }); +} \ No newline at end of file diff --git a/docs/header.html b/docs/header.html index dfce9a1..fbe464c 100644 --- a/docs/header.html +++ b/docs/header.html @@ -29,10 +29,11 @@ - + $treeview diff --git a/include/ikarus/folders/blueprint_folder.h b/include/ikarus/folders/blueprint_folder.h index 8ab973e..430c147 100644 --- a/include/ikarus/folders/blueprint_folder.h +++ b/include/ikarus/folders/blueprint_folder.h @@ -1,21 +1,150 @@ #pragma once /// \file blueprint_folder.h -/// \author Folling +/// \author Folling -#include #include -/// \addtogroup folder Folders +/// \addtogroup blueprints Blueprints /// @{ IKARUS_BEGIN_HEADER /// \brief A blueprint folder, storing blueprints and other blueprint folders. -struct IkarusBlueprintFolder { - /// \private \brief The id of the blueprint folder. - IkarusId id; -}; +struct IkarusBlueprintFolder; + +/// \brief Creates a blueprint folder. +/// \param project The project the blueprint folder is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the blueprint folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created blueprint folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusBlueprintFolder * ikarus_blueprint_folder_create( + struct IkarusProject * project, IkarusBlueprintFolder * parent, size_t position, char const * name +); + +/// \brief Copies a blueprint folder. +/// \details Creates a copy of the blueprint folder without its children. +/// \param blueprint_folder The blueprint folder to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the blueprint folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created blueprint folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusBlueprintFolder * ikarus_blueprint_folder_copy( + IkarusBlueprintFolder * blueprint_folder, IkarusBlueprintFolder * parent, size_t position, char const * name +); + +/// \brief Deletes a blueprint folder and all its children +/// \param blueprint_folder The blueprint folder to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param keep_children If true, the children of the blueprint folder will be moved to the parent folder. +IKA_API void ikarus_blueprint_folder_delete(IkarusBlueprintFolder * blueprint_folder, bool keep_children); + +/// \brief Gets the project of a blueprint folder. +/// \param blueprint_folder The blueprint folder to get the project of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The project of the blueprint folder or null if an error occurs. +IKA_API struct IkarusProject * ikarus_blueprint_folder_get_project(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Gets the parent folder of a blueprint folder. +/// \param blueprint_folder The blueprint folder to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the blueprint folder or null if an error occurs. +IKA_API struct IkarusBlueprintFolder * ikarus_blueprint_folder_get_parent(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Gets the position of a blueprint folder within its parent folder. +/// \param blueprint_folder The blueprint folder to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the blueprint folder or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_folder_get_position(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Gets the name of a blueprint folder. +/// \param blueprint_folder The blueprint folder to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the blueprint folder or null if an error occurs. +/// \remark The returned pointer is valid until the blueprint folder is freed but may be invalidated by other operations. +IKA_API char const * ikarus_blueprint_folder_get_name(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Gets the number of children of a blueprint folder. +/// \param blueprint_folder The blueprint folder to get the number of children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of children or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_folder_get_child_count(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Gets the children of a blueprint folder. +/// \param blueprint_folder The blueprint folder to get the children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param children_out The buffer to write the children to. +/// \pre \li Must not be null. +/// \param children_out_size The size of the buffer. +IKA_API void ikarus_blueprint_folder_get_children( + IkarusBlueprintFolder const * blueprint_folder, struct IkarusBlueprintTreeItem ** children_out, size_t children_out_size +); + +/// \brief Sets the parent folder of an blueprint folder. +/// \param blueprint_folder The blueprint folder to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the blueprint folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_blueprint_folder_set_parent( + IkarusBlueprintFolder * blueprint_folder, struct IkarusEntityFolder * new_parent, size_t new_position +); + +/// \brief Sets the position of an blueprint folder within its parent folder. +/// \param blueprint_folder The blueprint folder to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the blueprint folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_blueprint_folder_set_position(IkarusBlueprintFolder * blueprint_folder, size_t new_position); + +/// \brief Sets the name of an blueprint folder. +/// \param blueprint_folder The blueprint folder to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the blueprint folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_blueprint_folder_set_name(IkarusBlueprintFolder * blueprint_folder, char const * new_name); + +/// \brief Converts a blueprint folder to a generic folder. +/// \param blueprint_folder The blueprint folder to convert. +/// \return The constructed folder, representing the blueprint folder. +IKA_API struct IkarusFolder * ikarus_blueprint_folder_to_folder(IkarusBlueprintFolder const * blueprint_folder); + +/// \brief Converts a blueprint folder to an object. +/// \param blueprint_folder The blueprint folder to convert. +/// \return The constructed object, representing the blueprint folder. +IKA_API struct IkarusObject * ikarus_blueprint_folder_to_object(IkarusBlueprintFolder const * blueprint_folder); IKARUS_END_HEADER diff --git a/include/ikarus/folders/blueprint_tree_item.h b/include/ikarus/folders/blueprint_tree_item.h new file mode 100644 index 0000000..5a09dcc --- /dev/null +++ b/include/ikarus/folders/blueprint_tree_item.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file blueprint_tree_item.h +/// \author Folling + +#include + +/// \addtogroup blueprints Blueprints +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusBlueprintTreeItem; + +/// \brief Visits a blueprint tree item, calling the appropriate visitor function. +/// \param item The item to visit. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprint_visitor The visitor function called if the item is a blueprint. Skipped if null. +/// \param blueprint_folder_visitor The visitor function called if the item is a blueprint folder. Skipped if null. +/// \param data The data passed to the visitor functions. +IKA_API void ikarus_blueprint_tree_item_visit( + struct IkarusBlueprintTreeItem * item, + void (*blueprint_visitor)(struct IkarusBlueprint * blueprint, void * data), + void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder * folder, void * data), + void * data +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/folders/entity_folder.h b/include/ikarus/folders/entity_folder.h index 2bffa3f..8c516cb 100644 --- a/include/ikarus/folders/entity_folder.h +++ b/include/ikarus/folders/entity_folder.h @@ -1,21 +1,150 @@ #pragma once /// \file entity_folder.h -/// \author Folling +/// \author Folling -#include #include -/// \addtogroup folder Folders +/// \addtogroup entities Entities /// @{ IKARUS_BEGIN_HEADER /// \brief A entity folder, storing entities and other entity folders. -struct IkarusEntityFolder { - /// \private \brief The id of the entity folder. - IkarusId id; -}; +struct IkarusEntityFolder; + +/// \brief Creates a entity folder. +/// \param project The project the entity folder is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the entity folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created entity folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusEntityFolder * ikarus_entity_folder_create( + struct IkarusProject * project, IkarusEntityFolder * parent, size_t position, char const * name +); + +/// \brief Copies a entity folder. +/// \details Creates a copy of the entity folder without its children. +/// \param entity_folder The entity folder to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the entity folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created entity folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusEntityFolder * ikarus_entity_folder_copy( + IkarusEntityFolder * entity_folder, IkarusEntityFolder * parent, size_t position, char const * name +); + +/// \brief Deletes a entity folder and all its children +/// \param entity_folder The entity folder to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param keep_children If true, the children of the entity folder will be moved to the parent folder. +IKA_API void ikarus_entity_folder_delete(IkarusEntityFolder * entity_folder, bool keep_children); + +/// \brief Gets the project of a entity folder. +/// \param entity_folder The entity folder to get the project of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The project of the entity folder or null if an error occurs. +IKA_API struct IkarusProject * ikarus_entity_folder_get_project(IkarusEntityFolder const * entity_folder); + +/// \brief Gets the parent folder of a entity folder. +/// \param entity_folder The entity folder to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the entity folder or null if an error occurs. +IKA_API struct IkarusEntityFolder * ikarus_entity_folder_get_parent(IkarusEntityFolder const * entity_folder); + +/// \brief Gets the position of a entity folder within its parent folder. +/// \param entity_folder The entity folder to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the entity folder or undefined if an error occurs. +IKA_API size_t ikarus_entity_folder_get_position(IkarusEntityFolder const * entity_folder); + +/// \brief Gets the name of a entity folder. +/// \param entity_folder The entity folder to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the entity folder or null if an error occurs. +/// \remark The returned pointer is valid until the entity folder is freed but may be invalidated by other operations. +IKA_API char const * ikarus_entity_folder_get_name(IkarusEntityFolder const * entity_folder); + +/// \brief Gets the number of children of a entity folder. +/// \param entity_folder The entity folder to get the number of children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of children or undefined if an error occurs. +IKA_API size_t ikarus_entity_folder_get_child_count(IkarusEntityFolder const * entity_folder); + +/// \brief Gets the children of a entity folder. +/// \param entity_folder The entity folder to get the children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param children_out The buffer to write the children to. +/// \pre \li Must not be null. +/// \param children_out_size The size of the buffer. +IKA_API void ikarus_entity_folder_get_children( + IkarusEntityFolder const * entity_folder, struct IkarusEntityTreeItem ** children_out, size_t children_out_size +); + +/// \brief Sets the parent folder of an entity folder. +/// \param entity_folder The entity folder to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the entity folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_entity_folder_set_parent( + IkarusEntityFolder * entity_folder, struct IkarusEntityFolder * new_parent, size_t new_position +); + +/// \brief Sets the position of an entity folder within its parent folder. +/// \param entity_folder The entity folder to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the entity folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_entity_folder_set_position(IkarusEntityFolder * entity_folder, size_t new_position); + +/// \brief Sets the name of an entity folder. +/// \param entity_folder The entity folder to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the entity folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_entity_folder_set_name(IkarusEntityFolder * entity_folder, char const * new_name); + +/// \brief Converts a entity folder to a generic folder. +/// \param entity_folder The entity folder to convert. +/// \return The constructed folder, representing the entity folder. +IKA_API struct IkarusFolder * ikarus_entity_folder_to_folder(IkarusEntityFolder const * entity_folder); + +/// \brief Converts a entity folder to an object. +/// \param entity_folder The entity folder to convert. +/// \return The constructed object, representing the entity folder. +IKA_API struct IkarusObject * ikarus_entity_folder_to_object(IkarusEntityFolder const * entity_folder); IKARUS_END_HEADER diff --git a/include/ikarus/folders/entity_tree_item.h b/include/ikarus/folders/entity_tree_item.h new file mode 100644 index 0000000..516455f --- /dev/null +++ b/include/ikarus/folders/entity_tree_item.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file entity_tree_item.h +/// \author Folling + +#include + +/// \addtogroup entities Entities +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusEntityTreeItem; + +/// \brief Visits a entity tree item, calling the appropriate visitor function. +/// \param item The item to visit. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param entity_visitor The visitor function called if the item is a entity. Skipped if null. +/// \param entity_folder_visitor The visitor function called if the item is a entity folder. Skipped if null. +/// \param data The data passed to the visitor functions. +IKA_API void ikarus_entity_tree_item_visit( + struct IkarusEntityTreeItem * item, + void (*entity_visitor)(struct IkarusEntity * entity, void * data), + void (*entity_folder_visitor)(struct IkarusEntityFolder * folder, void * data), + void * data +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/folders/folder.h b/include/ikarus/folders/folder.h index 0cdf946..d0d60e2 100644 --- a/include/ikarus/folders/folder.h +++ b/include/ikarus/folders/folder.h @@ -1,7 +1,7 @@ #pragma once /// \file folder.h -/// \author Folling +/// \author Folling #include #include @@ -15,61 +15,23 @@ IKARUS_BEGIN_HEADER /// \brief Folders are used to group objects together. /// @{ -/// \private \brief The data of a folder. -union IkarusFolderData { - /// \private \brief The blueprint folder data of the folder. - IkarusBlueprintFolder blueprint_folder; - /// \private \brief The property folder data of the folder. - IkarusPropertyFolder property_folder; - /// \private \brief The entity folder data of the folder. - IkarusEntityFolder entity_folder; -}; - /// \brief A generic folder. Similar to how Objects wrap all types of objects, Folders wrap all types of folders. -struct IkarusFolder { - /// \private \brief The data of the folder. - IkarusFolderData data; +struct IkarusFolder; - /// \private \brief The type of the folder. - IkarusFolderType type; -}; - -/// \brief Constructs a folder from a blueprint folder. -/// \param blueprint_folder The blueprint folder to construct the folder from. -/// \return The constructed folder. -IKA_API IkarusFolder ikarus_folder_from_blueprint_folder(IkarusBlueprintFolder blueprint_folder); -/// \brief Constructs a folder from a property folder. -/// \param property_folder The property folder to construct the folder from. -/// \return The constructed folder. -IKA_API IkarusFolder ikarus_folder_from_property_folder(IkarusPropertyFolder property_folder); -/// \brief Constructs a folder from an entity folder. -/// \param entity_folder The entity folder to construct the folder from. -/// \return The constructed folder. -IKA_API IkarusFolder ikarus_folder_from_entity_folder(IkarusEntityFolder entity_folder); - -/// \brief Fetches the folder type of a folder. -/// \param folder The folder to fetch the type of. -/// \return The type of the folder. -IKA_API IkarusFolderType ikarus_folder_get_type(IkarusFolder folder); - -/// \brief Checks if two folders are equal. -/// \details Since ids store the type of the object, this boils down to a simple comparison of the ids. -/// \param left The left side of the comparison. -/// \param right The right side of the comparison. -/// \return True if the folders are equal, false otherwise -IKA_API bool ikarus_folder_is_equal(IkarusFolder left, IkarusFolder right); +/// \brief Special value for inserting objects at the end of a folder. +enum FolderPosition { FolderPosition_EndOfFolder = -1 }; /// \brief Visits a folder. Calling the appropriate function for the folder's type. /// \param folder The folder to visit. -/// \param blueprint The function to call if the folder is a blueprint folder. -/// \param property The function to call if the folder is a property folder. -/// \param entity The function to call if the folder is an entity folder. -/// \param data The data to pass to the functions. +/// \param blueprint_visitor The function to call if the folder is a blueprint folder. +/// \param property_visitor The function to call if the folder is a property folder. +/// \param entity_visitor The function to call if the folder is an entity folder. +/// \param data The data passed to the visitor functions. IKA_API void ikarus_folder_visit( - IkarusFolder folder, - void (*blueprint)(IkarusBlueprintFolder, void *), - void (*property)(IkarusPropertyFolder, void *), - void (*entity)(IkarusEntityFolder, void *), + IkarusFolder * folder, + void (*blueprint_visitor)(IkarusBlueprintFolder *, void *), + void (*property_visitor)(IkarusPropertyFolder *, void *), + void (*entity_visitor)(IkarusEntityFolder *, void *), void * data ); diff --git a/include/ikarus/folders/folder_type.h b/include/ikarus/folders/folder_type.h index 2a63e46..d3ac05e 100644 --- a/include/ikarus/folders/folder_type.h +++ b/include/ikarus/folders/folder_type.h @@ -3,7 +3,7 @@ // IMPLEMENTATION_DETAIL_FOLDER_TYPES /// \file folder_type.h -/// \author Folling +/// \author Folling #include @@ -11,16 +11,16 @@ /// @{ /// \brief The type of an folder. -/// \remark Folders have the 8th bit set. +/// \remark The values are identical to their counterparts in #IkarusObjectType. enum IkarusFolderType { /// \brief Not a folder or no folder. IkarusFolderType_None = 0, /// \brief An IkarusBlueprintFolder - IkarusFolderType_BlueprintFolder = 0b1000'0001, + IkarusFolderType_BlueprintFolder = 0b0100'0001, /// \brief An IkarusPropertyFolder - IkarusFolderType_PropertyFolder = 0b1000'0010, + IkarusFolderType_PropertyFolder = 0b0100'0010, /// \brief An IkarusEntityFolder - IkarusFolderType_EntityFolder = 0b1000'0011, + IkarusFolderType_EntityFolder = 0b0100'0011, }; /// @} diff --git a/include/ikarus/folders/property_folder.h b/include/ikarus/folders/property_folder.h index 8e6709f..d55d6cf 100644 --- a/include/ikarus/folders/property_folder.h +++ b/include/ikarus/folders/property_folder.h @@ -1,22 +1,158 @@ #pragma once -#include +/// \file property_folder.h +/// \author Folling + #include -/// \file property_folder.h -/// \author Folling - -/// \addtogroup folder Folders +/// \addtogroup properties Properties /// @{ IKARUS_BEGIN_HEADER /// \brief A property folder, storing properties and other property folders. -/// \remark Property folders are scoped to the blueprint or entity they are associated with. -struct IkarusPropertyFolder { - /// \private \brief The id of the property folder. - IkarusId id; -}; +struct IkarusPropertyFolder; + +/// \brief Creates a property folder. +/// \param project The project the property folder is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the property folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created property folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusPropertyFolder * ikarus_property_folder_create( + struct IkarusProject * project, IkarusPropertyFolder * parent, size_t position, char const * name +); + +/// \brief Copies a property folder. +/// \details Creates a copy of the property folder without its children. +/// \param property_folder The property folder to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the property folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created property folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusPropertyFolder * ikarus_property_folder_copy( + IkarusPropertyFolder * property_folder, IkarusPropertyFolder * parent, size_t position, char const * name +); + +/// \brief Deletes a property folder and all its children +/// \param property_folder The property folder to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param keep_children If true, the children of the property folder will be moved to the parent folder. +IKA_API void ikarus_property_folder_delete(IkarusPropertyFolder * property_folder, bool keep_children); + +/// \brief Gets the project of a property folder. +/// \param property_folder The property folder to get the project of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The project of the property folder or null if an error occurs. +IKA_API struct IkarusProject * ikarus_property_folder_get_project(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the property source of a property folder. +/// \param property_folder The property folder to get the property source of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The property source of the property folder or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertySource * ikarus_property_folder_get_source(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the parent folder of a property folder. +/// \param property_folder The property folder to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the property folder or null if an error occurs. +IKA_API struct IkarusPropertyFolder * ikarus_property_folder_get_parent(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the position of a property folder within its parent folder. +/// \param property_folder The property folder to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the property folder or undefined if an error occurs. +IKA_API size_t ikarus_property_folder_get_position(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the name of a property folder. +/// \param property_folder The property folder to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the property folder or null if an error occurs. +/// \remark The returned pointer is valid until the property folder is freed but may be invalidated by other operations. +IKA_API char const * ikarus_property_folder_get_name(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the number of children of a property folder. +/// \param property_folder The property folder to get the number of children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of children or undefined if an error occurs. +IKA_API size_t ikarus_property_folder_get_child_count(IkarusPropertyFolder const * property_folder); + +/// \brief Gets the children of a property folder. +/// \param property_folder The property folder to get the children of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param children_out The buffer to write the children to. +/// \pre \li Must not be null. +/// \param children_out_size The size of the buffer. +IKA_API void ikarus_property_folder_get_children( + IkarusPropertyFolder const * property_folder, struct IkarusPropertyTreeItem ** children_out, size_t children_out_size +); + +/// \brief Sets the parent folder of an property folder. +/// \param property_folder The property folder to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the property folder in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_property_folder_set_parent( + IkarusPropertyFolder * property_folder, struct IkarusPropertyFolder * new_parent, size_t new_position +); + +/// \brief Sets the position of an property folder within its parent folder. +/// \param property_folder The property folder to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the property folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_property_folder_set_position(IkarusPropertyFolder * property_folder, size_t new_position); + +/// \brief Sets the name of an property folder. +/// \param property_folder The property folder to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the property folder. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_property_folder_set_name(IkarusPropertyFolder * property_folder, char const * new_name); + +/// \brief Converts a property folder to a generic folder. +/// \param property_folder The property folder to convert. +/// \return The constructed folder, representing the property folder. +IKA_API struct IkarusFolder * ikarus_property_folder_to_folder(IkarusPropertyFolder const * property_folder); + +/// \brief Converts a property folder to an object. +/// \param property_folder The property folder to convert. +/// \return The constructed object, representing the property folder. +IKA_API struct IkarusObject * ikarus_property_folder_to_object(IkarusPropertyFolder const * property_folder); IKARUS_END_HEADER diff --git a/include/ikarus/folders/property_tree_item.h b/include/ikarus/folders/property_tree_item.h new file mode 100644 index 0000000..0b37c2c --- /dev/null +++ b/include/ikarus/folders/property_tree_item.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file property_tree_item.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusPropertyTreeItem; + +/// \brief Visits a property tree item, calling the appropriate visitor function. +/// \param item The item to visit. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property_visitor The visitor function called if the item is a property. Skipped if null. +/// \param property_folder_visitor The visitor function called if the item is a property folder. Skipped if null. +/// \param data The data passed to the visitor functions. +IKA_API void ikarus_property_tree_item_visit( + struct IkarusPropertyTreeItem * item, + void (*property_visitor)(struct IkarusProperty * property, void * data), + void (*property_folder_visitor)(struct IkarusPropertyFolder * folder, void * data), + void * data +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/global.h b/include/ikarus/global.h new file mode 100644 index 0000000..6d91227 --- /dev/null +++ b/include/ikarus/global.h @@ -0,0 +1,16 @@ +#pragma once + +/// \file memory.h +/// \author Folling + +#include + +/// \addtogroup global Global +/// \brief Information relevant to the entire library. +/// @{ + +/// \brief Frees a pointer allocated by ikarus. Every pointer returned by a function must be freed using this function unless +/// explicitly stated otherwise. +IKA_API void ikarus_free(void * ptr); + +/// @} diff --git a/include/ikarus/id.h b/include/ikarus/id.h deleted file mode 100644 index d184e63..0000000 --- a/include/ikarus/id.h +++ /dev/null @@ -1,66 +0,0 @@ -// IMPLEMENTATION_DETAIL_DATABASE - -/// \file id.h -/// \author Folling - -#pragma once - -#include -#include -#include - -IKARUS_BEGIN_HEADER - -/// \defgroup id Ids -/// \brief Ids are used to identify objects in the database. -/// \details They are stored as 64 bit integers with the following layout: -/// - first 8 bits: #IkarusObjectType - 255 possible values, 0 for special values -/// - last 56 bits: incremented counter generated by the database -/// @{ - -/// \brief A wrapper around a 64 bit integer that represents the id of an object. -/// \details They are stored as 64 bit integers with the following layout: -/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. -/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. -/// - next 7 bits: #IkarusObjectType - 127 possible values, 0 for special values -/// - last 56 bits: incremented counter generated by the database -struct IkarusId { - /// \private \brief The value of the id. - int64_t data; -}; - -/// \brief A special id returned by failed functions. -IkarusId const IKARUS_ID_NONE{0}; -/// \brief A special id used to indicate an optional id not being specified. -IkarusId const IKARUS_ID_UNSPECIFIED{1}; - -/// \private \brief Generates a new id for the given object type. -/// \param data The data from which the id will be constructed. -/// \return The generated id. -/// \pre data must be valid under the format described in Id. It should also point to an object in the database. -IkarusId ikarus_id_from_data(int64_t data); - -/// \brief Checkes whether two ids are equal -/// \param left the left side of the comparison -/// \param right the right side of the comparison -/// \return True if the bits from the left id are equal to the bits of the right id -bool ikarus_id_is_equal(IkarusId left, IkarusId right); - -/// \brief Fetches the object type of the given id. -/// \param id The id to fetch the object type for. -/// \return The object type of the given id. -IKA_API IkarusObjectType ikarus_id_get_object_type(IkarusId id); - -/// \brief Checks if the given id is IKARUS_ID_NONE. -/// \param id The id to check. -/// \return True if the id is IKARUS_ID_NONE, false otherwise. -IKA_API bool ikarus_id_is_none(IkarusId id); - -/// \brief Checks if the given id is IKARUS_ID_UNSPECIFIED. -/// \param id The id to check. -/// \return True if the id is IKARUS_ID_UNSPECIFIED, false otherwise. -IKA_API bool ikarus_id_is_unspecified(IkarusId id); - -/// @} - -IKARUS_END_HEADER diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index bd0abd3..e47b1df 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -1,23 +1,210 @@ #pragma once /// \file blueprint.h -/// \author Folling +/// \author Folling -#include #include +#include /// \defgroup blueprints Blueprints /// \brief Blueprints are templates for entities. +/// @{ IKARUS_BEGIN_HEADER /// \brief A blueprint object. /// \details A blueprint is a collection of properties which can be linked to entities. -/// Each entity the blueprint is linked to will have values for the blueprints properties. -struct IkarusBlueprint { - /// \private \brief The id of the blueprint. - IkarusId id; -}; +/// Each entity the blueprint is linked to will have values for the blueprints properties.q +struct IkarusBlueprint; + +/// \brief Creates a blueprint. +/// \param project The project the blueprint is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the blueprint in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created blueprint or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusBlueprint * ikarus_blueprint_create( + struct IkarusProject * project, struct IkarusBlueprintFolder * parent, size_t position, char const * name +); + +/// \brief Creates a blueprint from an entity. +/// \details The created blueprint will have the same properties as the entity. +/// \param entity The entity to create the blueprint from. +/// \pre \li Must not be null. +/// \param link_entity If true, the entity will be linked to the blueprint. If not they will remain separate. +/// \param parent The parent folder of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the blueprint in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the blueprint. Must not be empty. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created blueprint or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusBlueprint * ikarus_blueprint_create_from_entity( + struct IkarusEntity * entity, bool link_entity, struct IkarusBlueprintFolder * parent, size_t position, char const * name +); + +/// \brief Copies a blueprint. +/// \details Creates a deep copy of the blueprint including all of its properties. +/// \param blueprint The blueprint to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the blueprint in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created blueprint or null if an error occurs. +/// \remark Linked entities won't be copied. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusBlueprint * ikarus_blueprint_copy( + IkarusBlueprint const * blueprint, struct IkarusBlueprintFolder * parent, size_t position, char const * name +); + +/// \brief Deletes a blueprint. +/// \param blueprint The blueprint to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The blueprint must not be accessed after deletion. +IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); + +/// \brief Gets the project of a blueprint. +/// \param blueprint The blueprint to get the project of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The project of the blueprint or null if an error occurs. +IKA_API struct IkarusProject * ikarus_blueprint_get_project(IkarusBlueprint const * blueprint); + +/// \brief Gets the parent folder of a blueprint. +/// \param blueprint The blueprint to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the blueprint or null if an error occurs. +IKA_API struct IkarusBlueprintFolder * ikarus_blueprint_get_parent(IkarusBlueprint const * blueprint); + +/// \brief Gets the position of a blueprint within its parent folder. +/// \param blueprint The blueprint to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the blueprint or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_get_position(IkarusBlueprint const * blueprint); + +/// \brief Gets the name of a blueprint. +/// \param blueprint The blueprint to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the blueprint or null if an error occurs. +/// \remark The returned pointer is valid until the blueprint is freed but may be invalidated by other operations. +IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint); + +/// \brief Gets the property root folder of a blueprint. +/// \param blueprint The blueprint to get the root folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The root folder of all properties of the blueprint or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertyFolder * ikarus_blueprint_get_property_root_folder(IkarusBlueprint const * blueprint); + +/// \brief Gets the number of properties of a blueprint. +/// \param blueprint The blueprint to get the number of properties of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of properties or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint); + +/// \brief Gets the properties of a blueprint. +/// \param blueprint The blueprint to get the properties of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param properties_out The buffer to write the properties to. +/// \pre \li Must not be null. +/// \param properties_out_size The size of the buffer. +IKA_API void ikarus_blueprint_get_properties( + IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size +); + +/// \brief Gets the number of entities linked to a blueprint. +/// \param blueprint The blueprint to get the number of linked entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of linked entities or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint); + +/// \brief Gets the entities linked to a blueprint. +/// \param blueprint The blueprint to get the linked entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param entities_out The buffer to write the entities to. +/// \pre \li Must not be null. +/// \param entities_out_size The size of the buffer. +IKA_API void ikarus_blueprint_get_linked_entities( + IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size +); + +/// \brief Sets the parent folder of a blueprint. +/// \param blueprint The blueprint to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the blueprint in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_blueprint_set_parent( + IkarusBlueprint * blueprint, struct IkarusBlueprintFolder * new_parent, size_t new_position +); + +/// \brief Sets the position of a blueprint within its parent folder. +/// \param blueprint The blueprint to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the blueprint. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_blueprint_set_position(IkarusBlueprint * blueprint, size_t new_position); + +/// \brief Sets the name of a blueprint. +/// \param blueprint The blueprint to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_blueprint_set_name(IkarusBlueprint * blueprint, char const * new_name); + +/// \brief Converts a blueprint to an object. +/// \param blueprint The blueprint to convert. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The constructed object, representing the blueprint. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint const * blueprint); + +/// \brief Compares two blueprints. +/// \param left The left blueprint to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param right The right blueprint to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return True if the two blueprints are equal, false otherwise. +/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two +/// blueprints reference the same blueprint in the same project. +IKA_API bool ikarus_blueprint_is_equal(IkarusBlueprint const * left, IkarusBlueprint const * right); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 980c03e..a24cf79 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,10 +1,9 @@ #pragma once -#include #include /// \file entity.h -/// \author Folling +/// \author Folling /// \defgroup entities Entities /// \brief Entities are the core building blocks of Ikarus. @@ -17,7 +16,7 @@ IKARUS_BEGIN_HEADER /// /// Properties can be associated with Entities in two ways: /// - Directly: The property is linked to the entity. -/// - Indirectly: The property is linked to a blueprint of the entity. +/// - Indirectly: The property is linked to a blueprint the entity is linked to. /// /// For each property an entity is linked to, it has a value. These values depend on the property's type. /// For more information on the types see the property documentation. @@ -31,10 +30,223 @@ IKARUS_BEGIN_HEADER /// \remark Values are guaranteed to be in valid format for a given type /// but not guaranteed to be valid under the settings of the property. /// This is because changing the settings can invalidate existing values without resetting them. -struct IkarusEntity { - /// \private \brief The ID of the entity. - IkarusId id; -}; +struct IkarusEntity; + +/// \brief Creates an entity. +/// \param project The project the entity is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the entity. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the entity in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the entity. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param blueprints Blueprints to link the entity to (0..n). Null is treated as an empty array. +/// \param blueprints_count The number of blueprints. +/// \return The created entity or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusEntity * ikarus_entity_create( + struct IkarusProject * project, + struct IkarusEntityFolder * parent, + size_t position, + char const * name, + struct IkarusBlueprint ** blueprints, + size_t blueprints_count +); + +/// \brief Copies an entity. +/// \details Creates a deep copy of the entity including all of its properties & associated values. +/// \param entity The entity to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent The parent folder of the entity. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the entity in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the entity. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created entity or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API IkarusEntity * ikarus_entity_copy( + struct IkarusEntity * entity, struct IkarusEntityFolder * parent, size_t position, char const * name +); + +/// \brief Deletes an entity. +/// \param entity The entity to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The entity must not be accessed after deletion. +IKA_API void ikarus_entity_delete(IkarusEntity * entity); + +IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint); + +/// \brief Checks if an entity has a specific property. +/// \param entity The entity to check. +/// \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 exist. +/// \return True if the entity has the property, false otherwise. +IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property); + +/// \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. +/// \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); + +/// \brief Unlinks an entity from a blueprint. All values of the properties of the blueprint the entity is linked with 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. +/// \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); + +/// \brief Gets the project of an entity. +/// \param entity The entity to get the project of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The project of the entity or null if an error occurs. +IKA_API struct IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity); + +/// \brief Gets the parent folder of an entity. +/// \param entity The entity to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the entity or null if an error occurs. +IKA_API struct IkarusEntityFolder * ikarus_entity_get_parent(IkarusEntity const * entity); + +/// \brief Gets the position of an entity within its parent folder. +/// \param entity The entity to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the entity or undefined if an error occurs. +IKA_API size_t ikarus_entity_get_position(IkarusEntity const * entity); + +/// \brief Gets the name of an entity. +/// \param entity The entity to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the entity or null if an error occurs. +/// \remark The returned pointer is valid until the entity is freed but may be invalidated by other operations. +IKA_API char const * ikarus_entity_get_name(IkarusEntity const * entity); + +/// \brief Gets the property root folder of an entity. +/// \param entity The entity to get the root folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The root folder of all properties of the entity or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertyFolder * ikarus_entity_get_property_root_folder(IkarusEntity const * entity); + +/// \brief Gets the number of properties of an entity. +/// \param entity The entity to get the number of properties of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of properties or undefined if an error occurs. +IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity); + +/// \brief Gets the properties of an entity. +/// \param entity The entity to get the properties of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param properties_out The buffer to write the properties to. +/// \pre \li Must not be null. +/// \param properties_out_size The size of the buffer. +IKA_API void ikarus_entity_get_properties( + IkarusEntity const * entity, struct IkarusProperty ** properties_out, size_t properties_out_size +); + +/// \brief Gets the value of a property of an entity. +/// \details If the entity has never set the value of the property, the default value is returned (which may be indeterminate). +/// \param entity The entity to get the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to get the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The value of the property or null if the entity does not have the property or an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusEntityValue * get_value(IkarusEntity const * entity, struct IkarusProperty const * property); + +/// \brief Sets the parent folder of an entity. +/// \param entity The entity to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the entity. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the entity in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_entity_set_parent(IkarusEntity * entity, struct IkarusEntityFolder * new_parent, size_t new_position); + +/// \brief Sets the position of an entity within its parent folder. +/// \param entity The entity to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the entity. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_entity_set_position(IkarusEntity * entity, size_t new_position); + +/// \brief Sets the name of an entity. +/// \param entity The entity to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the entity. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_entity_set_name(IkarusEntity * entity, char const * new_name); + +/// \brief Sets the value of a property of an entity. +/// \param entity The entity to set the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to set the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param value The new value of the property. +/// \pre \li Must not be null. +/// \pre \li Must be of the same type as the property. +/// \param validate_settings If set, this function fails not only if the type of the value is invalid, but also if it's not +/// valid under the properties settings. \see property.h +/// \remark If the entity does not have the property, this function fails. +IKA_API void ikarus_entity_set_value( + IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue * value, bool validate_settings +); + +/// \brief Converts an entity to an object. +/// \param entity The entity to convert. +/// \return The constructed object, representing the entity. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity const * entity); + +/// \brief Compares two entities. +/// \param left The left entity to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param right The right entity to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return True if the two entities are equal, false otherwise. +/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two +/// entities reference the same entity in the same project. +IKA_API bool ikarus_entity_is_equal(IkarusEntity const * left, IkarusEntity const * right); IKARUS_END_HEADER diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index ef8c592..256e8a0 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -1,16 +1,9 @@ #pragma once /// \file object.h -/// \author Folling +/// \author Folling -#include -#include -#include #include -#include -#include -#include -#include /// \defgroup object Objects /// \brief Objects are a compound type of all types of objects in the database. @@ -25,96 +18,26 @@ IKARUS_BEGIN_HEADER -struct IkarusFolder; - -/// \private \brief The data of an object. -union IkarusObjectData { - /// \private \brief The blueprint data of the object. - IkarusBlueprint blueprint; - /// \private \brief The property data of the object. - IkarusProperty property; - /// \private \brief The entity data of the object. - IkarusEntity entity; - /// \private \brief The blueprint folder data of the object. - IkarusBlueprintFolder blueprint_folder; - /// \private \brief The property folder data of the object. - IkarusPropertyFolder property_folder; - /// \private \brief The entity folder data of the object. - IkarusEntityFolder entity_folder; -}; - /// \brief A generic object. Wraps all types of objects, including folders. -struct IkarusObject { - /// \private \brief The type of the object. - IkarusObjectType type; - /// \private \brief The data of the object. - IkarusObjectData data; -}; - -/// \brief Constructs an object from a blueprint. -/// \param blueprint The blueprint to construct the object from. -/// \return The constructed object, representing the blueprint. -IKA_API IkarusObject ikarus_object_from_blueprint(IkarusBlueprint blueprint); - -/// \brief Constructs an object from a property. -/// \param property The property to construct the object from. -/// \return The constructed object, representing the property. -IKA_API IkarusObject ikarus_object_from_property(IkarusProperty property); - -/// \brief Constructs an object from an entity. -/// \param entity The entity to construct the object from. -/// \return The constructed object, representing the entity. -IKA_API IkarusObject ikarus_object_from_entity(IkarusEntity entity); - -/// \brief Constructs an object from a blueprint folder. -/// \param blueprint The folder to construct the object from. -/// \return The constructed object, representing the folder. -IKA_API IkarusObject ikarus_object_from_blueprint_folder(IkarusBlueprintFolder folder); - -/// \brief Constructs an object from a property folder. -/// \param property The folder to construct the object from. -/// \return The constructed object, representing the folder. -IKA_API IkarusObject ikarus_object_from_property_folder(IkarusPropertyFolder folder); - -/// \brief Constructs an object from a entity folder. -/// \param entity The folder to construct the object from. -/// \return The constructed object, representing the folder. -IKA_API IkarusObject ikarus_object_from_entity_folder(IkarusEntityFolder folder); - -/// \brief Constructs an object from a folder. -/// \param folder The folder to construct the object from. -/// \return The constructed object, representing the folder. -IKA_API IkarusObject ikarus_object_from_folder(IkarusFolder folder); - -/// \brief Compares two objects for equality. -/// \details Since ids store the type of the object, this boils down to a simple comparison of the ids. -/// \param left The left side of the comparison. -/// \param right The right side of the comparison. -/// \return Whether the objects are equal. -IKA_API bool ikarus_object_is_equal(IkarusObject left, IkarusObject right); - -/// \brief Fetches the type of an object. -/// \param object The object to fetch the type of. -/// \return The type of the object. -IKA_API IkarusObjectType ikarus_object_get_type(IkarusObject object); +struct IkarusObject; /// \brief Visits an object. Calling the appropriate function for the object's type. /// \param object The object to visit. -/// \param blueprint The function to call if the object is a blueprint. -/// \param property The function to call if the object is a property. -/// \param entity The function to call if the object is an entity. -/// \param blueprint_folder The function to call if the object is a blueprint folder. -/// \param property_folder The function to call if the object is a property folder. -/// \param entity_folder The function to call if the object is an entity folder. -/// \param data The data to pass to the functions. +/// \param blueprint_visitor The function to call if the object is a blueprint. Skipped if null. +/// \param property_visitor The function to call if the object is a property. Skipped if null. +/// \param entity_visitor The function to call if the object is an entity. Skipped if null. +/// \param blueprint_folder_visitor The function to call if the object is a blueprint folder. Skipped if null. +/// \param property_folder_visitor The function to call if the object is a property folder. Skipped if null. +/// \param entity_folder_visitor The function to call if the object is an entity folder. Skipped if null. +/// \param data The data passed to the visitor functions. IKA_API void ikarus_object_visit( - IkarusObject object, - void (*visit_blueprint)(IkarusBlueprint, void *), - void (*visit_property)(IkarusProperty, void *), - void (*visit_entity)(IkarusEntity, void *), - void (*visit_blueprint_folder)(IkarusBlueprintFolder, void *), - void (*visit_property_folder)(IkarusPropertyFolder, void *), - void (*visit_entity_folder)(IkarusEntityFolder, void *), + IkarusObject * object, + void (*blueprint_visitor)(struct IkarusBlueprint *, void *), + void (*property_visitor)(struct IkarusProperty *, void *), + void (*entity_visitor)(struct IkarusEntity *, void *), + void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder *, void *), + void (*property_folder_visitor)(struct IkarusPropertyFolder *, void *), + void (*entity_folder_visitor)(struct IkarusEntityFolder *, void *), void * data ); diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 0fafc68..73f724b 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -1,38 +1,36 @@ #pragma once +/// \file object_type.h +/// \author Folling + +#include + +/// \addtogroup objects Objects +/// @{ + +IKARUS_BEGIN_HEADER + // IMPLEMENTATION_DETAIL_OBJECT_TYPES -/// \file object.h -/// \author Folling - -#include -#include - -/// \addtogroup object Objects -/// @{ - /// \brief The type of an object. -/// \remark Folders have the 4th bit set. +/// \remark The folder types are identical to their counterparts in #IkarusFolderType. enum IkarusObjectType { /// \brief Not an object or no object. IkarusObjectType_None = 0, /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 1, + IkarusObjectType_Blueprint = 0b00000001, /// \brief An IkarusProperty. - IkarusObjectType_Property = 2, + IkarusObjectType_Property = 0b00000010, /// \brief An IkarusEntity. - IkarusObjectType_Entity = 3, + IkarusObjectType_Entity = 0b00000011, /// \brief An IkarusBlueprintFolder - IkarusObjectType_BlueprintFolder = IkarusFolderType_BlueprintFolder, + IkarusObjectType_BlueprintFolder = 0b01000001, /// \brief An IkarusPropertyFolder - IkarusObjectType_PropertyFolder = IkarusFolderType_PropertyFolder, + IkarusObjectType_PropertyFolder = 0b01000010, /// \brief An IkarusEntityFolder - IkarusObjectType_EntityFolder = IkarusFolderType_EntityFolder, + IkarusObjectType_EntityFolder = 0b01000011, }; -/// \brief Constructs an IkarusObjectType from an IkarusFolderType. -/// \param type The IkarusFolderType of which to construct the IkarusObjectType from. -/// \return The constructed IkarusObjectType, representing the folder type. -IKA_API IkarusObjectType ikarus_object_type_from_folder_type(IkarusFolderType type); +IKARUS_END_HEADER /// @} diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index d104ac3..edbbdd1 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -1,17 +1,16 @@ #pragma once -// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION, IMPLEMENTATION_DETAIL_PROPERTY_TYPES +// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION /// \file property.h -/// \author Folling +/// \author Folling -#include #include -#include -#include +#include /// \defgroup properties Properties /// \brief Properties define the structure and types of data. +/// @{ IKARUS_BEGIN_HEADER @@ -50,23 +49,181 @@ IKARUS_BEGIN_HEADER /// Fetching the value for some property of some entity will return the property's default value if none is specified. /// This default value is specified when the property is created and can be updated later. /// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. -struct IkarusProperty { - /// \private \brief The id of the property. - IkarusId id; -}; +struct IkarusProperty; -/// \brief The type of a property. -/// \details Designates the type of data stored by the property as well as which settings are -/// available. -/// \see IkarusPropertySettings -enum IkarusPropertyType { - /// \brief A true/false boolean-like value. - IkarusPropertyType_Toggle, - /// \brief An arbitrary numeric value. - IkarusPropertyType_Number, - /// \brief An arbitrary textual value. - IkarusPropertyType_Text, -}; +/// \brief Creates a property +/// \param project The project the property is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property_source The property source the property is part of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent_folder The parent folder of the property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the property in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param property_info The info of the property. +/// \pre \li Must not be null. +/// \return The created property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusProperty * ikarus_property_create( + struct IkarusProject * project, + struct IkarusPropertySource * property_source, + struct IkarusPropertyFolder * parent_folder, + size_t position, + char const * name, + struct IkarusPropertyTypeInfo * property_info +); + +/// \brief Copies a property. +/// \details Creates a deep copy of the property including all of its settings and associated values. +/// \param property The property to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param source The source to copy the property to. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param parent_folder The parent folder of the property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param position The position of the property in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \param name The name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusProperty * ikarus_property_copy( + struct IkarusProperty * property, + struct IkarusPropertySource * source, + struct IkarusPropertyFolder * parent_folder, + size_t position, + char const * name +); + +/// \brief Deletes a property. +/// \param property The property to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The property must not be accessed after deletion. +IKA_API void ikarus_property_delete(struct IkarusProperty * property); + +/// \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. +/// \return The project of the property or null if an error occurs. +IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty const * property); + +/// \brief Gets the parent folder of a property. +/// \param property The property to get the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The parent folder of the property or null if an error occurs. +IKA_API struct IkarusPropertyFolder * ikarus_property_get_parent(IkarusProperty const * property); + +/// \brief Gets the position of a property within its parent folder. +/// \param property The property to get the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The position of the property or undefined if an error occurs. +IKA_API size_t ikarus_property_get_position(IkarusProperty const * property); + +/// \brief Gets the name of a property. +/// \param property The property to get the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The name of the property or null if an error occurs. +/// \remark The returned pointer is valid until the property is freed but may be invalidated by other operations. +IKA_API char const * ikarus_property_get_name(IkarusProperty const * property); + +/// \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. +/// \return The type info of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertyTypeInfo * ikarus_property_get_type_info(IkarusProperty const * property); + +/// \brief Gets the source of a property. +/// \param property The property to get the source of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The source of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertySource * ikarus_property_get_source(IkarusProperty const * property); + +/// \brief Gets the default value of a property. +/// \param property The property to get the default value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The default value of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); + +/// \brief Sets the parent folder of a property. +/// \param property The property to set the parent folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_parent The new parent folder of the property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the property in the parent folder. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of old and new siblings. +IKA_API void ikarus_property_set_parent( + IkarusProperty * property, struct IkarusPropertyFolder * new_parent, size_t new_position +); + +/// \brief Sets the position of a property within its parent folder. +/// \param property The property to set the position of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_position The new position of the property. \see #FolderPosition +/// \pre \li Must be within bounds for the parent folder. +/// \remark This adjusts the positions of siblings. +IKA_API void ikarus_property_set_position(IkarusProperty * property, size_t new_position); + +/// \brief Sets 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 new_name The new name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_property_set_name(IkarusProperty * property, char const * new_name); + +/// \brief Sets the type info of a property and resets all values to the new default value. +/// \param property The property to set the type info of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_type The new type info of the property. +/// \param attempt_conversion Whether to attempt to convert the property's values to the new type info. Conversion rules are +/// unspecified for now, but follow common sense. +IKA_API void ikarus_property_set_type_info( + IkarusProperty * property, struct IkarusPropertyTypeInfo new_type_info, bool attempt_conversion +); + +/// \brief Converts a property to an object. +/// \param property The property to convert. +/// \return The constructed object, representing the property. +IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty const * property); + +/// \brief Compares two properties. +/// \param left The left property to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param right The right property to compare. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return True if the two properties are equal, false otherwise. +/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two +/// properties reference the same property in the same project. +IKA_API bool ikarus_property_is_equal(IkarusProperty const * left, IkarusProperty const * right); IKARUS_END_HEADER diff --git a/include/ikarus/objects/property_source.h b/include/ikarus/objects/property_source.h new file mode 100644 index 0000000..4dbcd47 --- /dev/null +++ b/include/ikarus/objects/property_source.h @@ -0,0 +1,47 @@ +#pragma once + +/// \file property_source.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// @{ + +IKARUS_BEGIN_HEADER + +struct PropertySource; + +/// \brief Creates an blueprint property source. +/// \param blueprint The blueprint to create the property source for. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The created property source or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct PropertySource * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint); + +/// \brief Creates an entity property source. +/// \param entity The entity to create the property source for. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The created property source or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct PropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity); + +/// \brief Visits a property source, calling the appropriate callback. +/// \param property_source The property source to visit. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprint_visitor The callback to call if the source is a blueprint, skipped if null. +/// \param entity_visitor The callback to call if the source is an entity, skipped if null. +/// \param user_data User data to pass to the callbacks. +IKA_API void ikarus_property_source_visit( + struct PropertySource * property_source, + void (*blueprint_visitor)(struct IkarusBlueprint *, void *), + void (*entity_visitor)(struct IkarusEntity *, void *), + void * user_data +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/property_type.h b/include/ikarus/objects/property_type.h index 3709a37..e26b1f0 100644 --- a/include/ikarus/objects/property_type.h +++ b/include/ikarus/objects/property_type.h @@ -3,29 +3,33 @@ // IMPLEMENTATION_DETAIL_PROPERTY_TYPES /// \file property_type.h -/// \author Folling +/// \author Folling #include -IKARUS_BEGIN_HEADER - -/// \defgroup property_types Property Types -/// \brief Property Types delineate the type of data stored by a property. +/// \addtogroup properties Properties /// @{ +IKARUS_BEGIN_HEADER + /// \brief The type of a property. /// \details Designates the type of data stored by the property as well as which settings are /// available. -/// \see IkarusPropertySettings enum IkarusPropertyType { - /// \brief A true/false boolean-like value. + /// \brief A true/false boolean-esque value. IkarusPropertyType_Toggle, - /// \brief An arbitrary numeric value. + /// \brief A numeric value, limited to IEEE 80 bit floating point numbers. IkarusPropertyType_Number, - /// \brief An arbitrary textual value. + /// \brief An arbitrary UTF-8 textual value. IkarusPropertyType_Text, }; -/// @} +/// \brief Fetches the default value for a property type. +/// \remark Not to be confused with the default value of a property. See ikarus_property_get_default_value +/// \param type The property type. +/// \return The default value for the property type or null if an error occurs. +IKA_API struct IkarusValue * ikarus_property_type_get_default_default_value(IkarusPropertyType type); IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/property_type_info.h b/include/ikarus/objects/property_type_info.h new file mode 100644 index 0000000..cd7b6b9 --- /dev/null +++ b/include/ikarus/objects/property_type_info.h @@ -0,0 +1,79 @@ +#pragma once + +/// \file property_info.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief Information about a property. +/// \details Property information includes their type and settings consolidated to ascertain type safety. +struct IkarusPropertyTypeInfo; + +/// \brief Information about a toggle property. +struct IkarusTogglePropertyInfo; + +/// \brief Information about a number property. +struct IkarusNumberPropertyInfo; + +/// \brief Information about a text property. +struct IkarusTextPropertyInfo; + +/// \brief Creates a new toggle property info. +/// \return The created toggle property info. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTogglePropertyInfo * ikarus_toggle_property_info_create(); +/// \brief Sets the default value of a toggle property info. +/// \param toggle_property_info The toggle property info to set the default value of. +/// \pre \li Must not be null. +/// \param default_value The default value to set. +/// \pre \li Must not be null. +IKA_API void ikarus_toggle_property_info_set_default_value( + IkarusTogglePropertyInfo * toggle_property_info, struct IkarusToggleValue * default_value +); +/// \brief Converts a toggle property info to a generic property info. +/// \param toggle_property_info The toggle property info to convert. +/// \return The converted property info. +IKA_API IkarusPropertyTypeInfo * ikarus_toggle_property_info_to_property_info(IkarusTogglePropertyInfo * toggle_property_info); + +/// \brief Creates a new number property info. +/// \return The created number property info. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberPropertyInfo * ikarus_number_property_info_create(); +/// \brief Sets the default value of a number property info. +/// \param number_property_info The number property info to set the default value of. +/// \pre \li Must not be null. +/// \param default_value The default value to set. +/// \pre \li Must not be null. +IKA_API void ikarus_number_property_info_set_default_value( + IkarusNumberPropertyInfo * number_property_info, struct IkarusNumberValue * default_value +); +/// \brief Converts a number property info to a generic property info. +/// \param number_property_info The number property info to convert. +/// \return The converted property info. +IKA_API IkarusPropertyTypeInfo * ikarus_number_property_info_to_property_info(IkarusNumberPropertyInfo * number_property_info); + +/// \brief Creates a new text property info. +/// \return The created text property info. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextPropertyInfo * ikarus_text_property_info_create(); +/// \brief Sets the default value of a text property info. +/// \param text_property_info The text property info to set the default value of. +/// \pre \li Must not be null. +/// \param default_value The default value to set. +/// \pre \li Must not be null. +IKA_API void ikarus_text_property_info_set_default_value( + IkarusTextPropertyInfo * text_property_info, struct IkarusTextValue * default_value +); +/// \brief Converts a text property info to a generic property info. +/// \param text_property_info The text property info to convert. +/// \return The converted property info. +IKA_API IkarusPropertyTypeInfo * ikarus_text_property_info_to_property_info(IkarusTextPropertyInfo * text_property_info); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/project/project.h b/include/ikarus/project/project.h new file mode 100644 index 0000000..a041946 --- /dev/null +++ b/include/ikarus/project/project.h @@ -0,0 +1,179 @@ +#pragma once + +/// \file project.h +/// \author Folling + +#include +#include + +/// \defgroup projects Projects +/// \brief Projects are the persistence mechanism of Ikarus. Each project contains a set of objects. +/// \details Projects are stored on the filesystem. Each project has a name. +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief An Ikarus project. +/// \details A project may only be open once at a time. Opening a project from two different locations Gets undefined +/// behavior. +struct IkarusProject; + +/// \brief Creates a persisted project on the filesystem. +/// \param path The path to the project. +/// \pre \li Must not be null. +/// \pre \li Must point to a valid unused path on the system. +/// \param name The name of the project. Must neither be null nor empty. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created project or null if an error occurs. +/// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use +/// ikarus_project_delete +IKA_API IkarusProject * ikarus_project_create(char const * path, char const * name); + +/// \brief Creates a project in memory. +/// \param name The name of the project. Must neither be null nor empty. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \return The created project or null if an error occurs. +/// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use +/// ikarus_project_delete +IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name); + +/// \brief Opens an existing project. +/// \param path The path to the project. +/// \pre \li Must not be null. +/// \pre \li Must point to an existing project on the system. +/// \return The opened project or null if an error occurs. +/// \remark Must be freed using ikarus_free. Freeing does not delete the project from the filesystem. For that, use +/// ikarus_project_delete +IKA_API IkarusProject * ikarus_project_open(char const * path); + +/// \brief Copies a project to a new location. +/// \details The new project is not opened. +/// \param project The project to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param target_path The new location of the project. +/// \pre \li Must not be null. +/// \pre \li Must point to a valid unused path on the system. +/// \param target_name The name of the new project. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \remark If successful the project connection remains intact. The previous location will still exist. +IKA_API void ikarus_project_copy(IkarusProject const * project, char const * target_path, char const * target_name); + +/// \brief Deletes a project and all its associated data from the filesystem. +/// \param project The project to delete. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark also frees the project. +/// \remark In-Memory projects will just be freed. +/// \remark If deletion fails, the project pointer remains intact. +IKA_API void ikarus_project_delete(IkarusProject * project); + +/// \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. +/// \return The name of the project. +/// \remark Must be freed using #ikarus_free. +IKA_API char const * ikarus_project_get_name(IkarusProject const * project); + +/// \brief Sets the name of a project. +/// \param project The project to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the project. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_name); + +/// \brief Gets the path of a project. +/// \param project The project to get the path of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The path of the project. +/// \remark Must be freed using #ikarus_free. +IKA_API char const * ikarus_project_get_path(IkarusProject const * project); + +/// \brief Moves a project to a new location. +/// \param project The project to move. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param target_path The new location of the project. +/// \pre \li Must not be null. +/// \pre \li Must point to a valid unused path on the system. +/// \remark If successful the project connection remains intact. The previous location will not exist anymore. +/// \remark Due to the nature of filesystems this function may not be atomic. +IKA_API void ikarus_project_move(IkarusProject * project, char const * target_path); + +/// \brief Gets the error code of a project. +/// \param project The project to get the error code of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The error code of the project. +IKA_API int ikarus_project_get_error_code(IkarusProject const * project); +/// \brief Gets the error message of a project. +/// \param project The project to get the error message of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The error message of the project. +/// \remark The returned pointer is valid until the project is freed but may be altered by other operations. +/// \warning Must not be freed. +IKA_API char const * ikarus_project_get_error_message(IkarusProject const * project); + +/// \brief Gets the blueprint root folder of a project. +/// \param project The project to get the blueprint root folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The blueprint root folder of the project or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder(IkarusProject const * project); + +/// \brief Gets the number of blueprints of a project. +/// \param project The project to get the number of blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of blueprints or undefined if an error occurs. +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project); + +/// \brief Gets the blueprints of a project. +/// \param project The project to get the blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprints_out The buffer to write the blueprints to. +/// \pre \li Must not be null. +/// \param blueprints_out_size The size of the buffer. +IKA_API void ikarus_project_get_blueprints( + IkarusProject const * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size +); + +/// \brief Gets the entity root folder of a project. +/// \param project The project to get the entity root folder of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The entity root folder of the project or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(IkarusProject const * project); + +/// \brief Gets the number of entities of a project. +/// \param project The project to get the number of entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of entities or undefined if an error occurs. +IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project); + +/// \brief Gets the entities of a project. +/// \param project The project to get the entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param entities_out The buffer to write the entities to. +/// \pre \li Must not be null. +/// \param entities_out_size The size of the buffer. +IKA_API void ikarus_project_get_entities( + IkarusProject const * project, struct IkarusEntity ** entities_out, size_t entities_out_size +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/scopes/blueprint_scope.h b/include/ikarus/scopes/blueprint_scope.h index eac5eac..56fa7ec 100644 --- a/include/ikarus/scopes/blueprint_scope.h +++ b/include/ikarus/scopes/blueprint_scope.h @@ -1,7 +1,7 @@ #pragma once /// \file blueprint_scope.h -/// \author Folling +/// \author Folling #include #include @@ -12,14 +12,18 @@ IKARUS_BEGIN_HEADER /// \brief The global scope of all blueprints. -struct IkarusBlueprintScope { - /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. - short _dummy; -}; +struct IkarusBlueprintScope; /// \brief Creates a blueprint scope. /// \return The created blueprint scope. -IKA_API IkarusBlueprintScope ikarus_blueprint_scope_create(); +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusBlueprintScope * ikarus_blueprint_scope_create(); + +/// \brief Converts a blueprint scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +/// \remark Must be freed with #ikarus_free. +IKA_API struct IkarusObjectScope * ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope); IKARUS_END_HEADER diff --git a/include/ikarus/scopes/entity_scope.h b/include/ikarus/scopes/entity_scope.h index e29d725..e1838cc 100644 --- a/include/ikarus/scopes/entity_scope.h +++ b/include/ikarus/scopes/entity_scope.h @@ -1,9 +1,8 @@ #pragma once /// \file entity_scope.h -/// \author Folling +/// \author Folling -#include #include /// \addtogroup object_scopes ObjectScopes @@ -12,14 +11,18 @@ IKARUS_BEGIN_HEADER /// \brief The global scope of all entities. -struct IkarusEntityScope { - /// \private \brief Empty structs aren't allowed in C, so we need a dummy field. - short _dummy; -}; +struct IkarusEntityScope; -/// \brief Creates a entity scope. +/// \brief Creates an entity scope. /// \return The created entity scope. -IKA_API IkarusEntityScope ikarus_entity_scope_create(); +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusEntityScope * ikarus_entity_scope_create(); + +/// Converts an entity scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +/// \remark Must be freed with #ikarus_free. +IKA_API struct IkarusObjectScope * ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope); IKARUS_END_HEADER diff --git a/include/ikarus/scopes/object_scope.h b/include/ikarus/scopes/object_scope.h index 1c16d9f..69972cf 100644 --- a/include/ikarus/scopes/object_scope.h +++ b/include/ikarus/scopes/object_scope.h @@ -3,12 +3,9 @@ // IMPLEMENTATION_DETAIL_OBJECT_SCOPES /// \file object_scope.h -/// \author Folling +/// \author Folling #include -#include -#include -#include /// \defgroup object_scopes Object Scopes /// \brief Scopes define where objects belong to. @@ -17,65 +14,30 @@ IKARUS_BEGIN_HEADER -/// \private \brief The data for an object scope. -union IkarusObjectScopeData { - /// \private \brief The blueprint data of the scope. - IkarusBlueprintScope _blueprint; - /// \private \brief The property data of the scope. - IkarusPropertyScope _property; - /// \private \brief The entity data of the scope. - IkarusEntityScope _entity; -}; +/// \brief The scope of an object. +struct IkarusObjectScope; -/// The type of an object scope. +/// \brief The type of an object scope. enum IkarusObjectScopeType { /// \brief The scope is a blueprint scope. - IkarusObjectScopeType_Blueprint, + IkarusObjectScopeType_Blueprint = 1, /// \brief The scope is a property scope. - IkarusObjectScopeType_Property, + IkarusObjectScopeType_Property = 2, /// \brief The scope is an entity scope. - IkarusObjectScopeType_Entity + IkarusObjectScopeType_Entity = 3 }; -/// \brief The scope of an object. -struct IkarusObjectScope { - /// \private \brief Represents the type of the scope. - IkarusObjectScopeType _type; - /// \private \brief Represents the data of the scope. - IkarusObjectScopeData _data; -}; - -/// \brief Converts a blueprint scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope scope); - -/// \brief Converts a property scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope scope); - -/// Converts an entity scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -IKA_API IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope scope); - -/// \brief Fetches the type of an object scope. -/// \param scope The scope to fetch the type of. -/// \return The type of the scope. -IKA_API IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope scope); - /// \brief Visits an object scope, calling the appropriate function. /// \param scope The scope to visit. -/// \param blueprint The function to call if the scope is an #IkarusBlueprintScope. -/// \param property The function to call if the scope is an #IkarusPropertyScope. -/// \param entity The function to call if the scope is an #IkarusEntityScope. +/// \param blueprint_visitor The function to call if the scope is an #IkarusBlueprintScope. +/// \param property_visitor The function to call if the scope is an #IkarusPropertyScope. +/// \param entity_visitor The function to call if the scope is an #IkarusEntityScope. /// \remark function pointers may be null in which case they are not called. IKA_API void ikarus_object_scope_visit( - IkarusObjectScope scope, - void (*blueprint)(IkarusBlueprintScope, void *), - void (*property)(IkarusPropertyScope, void *), - void (*entity)(IkarusEntityScope, void *), + IkarusObjectScope * scope, + void (*blueprint_visitor)(struct IkarusBlueprintScope *, void *), + void (*property_visitor)(struct IkarusPropertyScope *, void *), + void (*entity_visitor)(struct IkarusEntityScope *, void *), void * data ); diff --git a/include/ikarus/scopes/property_scope.h b/include/ikarus/scopes/property_scope.h index 6de365c..d7b38db 100644 --- a/include/ikarus/scopes/property_scope.h +++ b/include/ikarus/scopes/property_scope.h @@ -1,63 +1,46 @@ #pragma once /// \file property_scope.h -/// \author Folling +/// \author Folling #include #include -#include -#include /// \addtogroup object_scopes ObjectScopes /// @{ IKARUS_BEGIN_HEADER -/// \brief Data for a property scope. This can either be a blueprint or an entity. -union IkarusPropertyScopeData { - /// \private \brief The blueprint the property is scoped to. - IkarusBlueprint _blueprint; - /// \private \brief The entity the property is scoped to. - IkarusEntity _entity; -}; - -/// \brief The type of a property scope. This can either be a blueprint or an entity. -enum IkarusPropertyScopeType { - /// \brief The property is scoped to a blueprint. - IkarusPropertyScopeType_Blueprint, - /// \brief The property is scoped to an entity. - IkarusPropertyScopeType_Entity -}; - /// \brief The scope of a property -struct IkarusPropertyScope { - /// \private \brief Represents the type of the scope. - IkarusPropertyScopeType _type; - /// \private \brief Represents the data of the scope. - IkarusPropertyScopeData _data; -}; +struct IkarusPropertyScope; /// \brief Creates a property scope from a blueprint. /// \param blueprint The blueprint the property is scoped to. /// \return The created property scope. -IKA_API IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint blueprint); -/// \brief Creates a property scope from a entity. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusPropertyScope * ikarus_property_scope_create_blueprint(struct IkarusBlueprint * blueprint); +/// \brief Creates a property scope from an entity. /// \param entity The entity the property is scoped to. /// \return The created property scope. -IKA_API IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity entity); +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusPropertyScope * ikarus_property_scope_create_entity(struct IkarusEntity * entity); -/// \brief Fetches the type of an property scope. -/// \param scope The scope to fetch the type of. -/// \return The type of the scope. -IKA_API IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope scope); +/// \brief Converts a property scope to an object scope. +/// \param scope The scope to convert. +/// \return The converted scope. +/// \remark Must be freed with #ikarus_free. +IKA_API struct IkarusObjectScope * ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope); /// \brief Visits a property scope, calling the appropriate function. /// \param scope The scope to to visit -/// \param blueprint The function to call if the property is scoped to a blueprint. -/// \param entity The function to call if the property is scoped to an entity. -/// \param data Optional data to pass to the functions. -void ikarus_property_scope_visit( - IkarusPropertyScope scope, void (*blueprint)(IkarusBlueprint, void *), void (*entity)(IkarusEntity, void *), void * data +/// \param blueprint_visitor The function to call if the property is scoped to a blueprint. +/// \param entity_visitor The function to call if the property is scoped to an entity. +/// \param data The data passed to the visitor functions. +IKA_API void ikarus_property_scope_visit( + IkarusPropertyScope * scope, + void (*blueprint_visitor)(struct IkarusBlueprint *, void *), + void (*entity_visitor)(struct IkarusEntity *, void *), + void * data ); IKARUS_END_HEADER diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h index d5f7ab2..6fa7e95 100644 --- a/include/ikarus/stdtypes.h +++ b/include/ikarus/stdtypes.h @@ -3,6 +3,7 @@ #ifdef __cplusplus #include #include +// NOLINTNEXTLINE(google-global-names-in-headers) using std::size_t; #else #include diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index e1457af..a30cf2d 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -3,10 +3,10 @@ // IMPLEMENTATION_DETAIL_PROPERTY_TYPES /// \file value.h -/// \author Folling +/// \author Folling #include -#include +#include #include IKARUS_BEGIN_HEADER @@ -15,149 +15,115 @@ IKARUS_BEGIN_HEADER /// \brief The values stored in entities. /// \details Each entity has a value for each property it is associated with. /// The value is of the type specified by the property and constrained by the property's settings. +/// A value may be indeterminate which means it is unknown or not specified. /// \see PropertyType PropertySettings /// @{ /// \brief A true/false boolean-like value. For example "IsDead". -struct IkarusToggleValue { - /// \private \brief The value of the property. - bool _value; -}; +struct IkarusToggleValue; /// \brief An arbitrary numeric value. For example "Age". -struct IkarusNumberValue { - /// \private \brief The value of the property. - long double _value; -}; +struct IkarusNumberValue; /// \brief An arbitrary textual value. For example "First Name". -struct IkarusTextValue { - /// \private \brief The value of the property. - char const * _value; -}; - -/// \private \brief The data for a value. -union IkarusEntityValueData { - /// \private \brief The value as a toggle. - IkarusToggleValue toggle; - /// \private \brief The value as a number. - IkarusNumberValue number; - /// \private \brief The value as text. - IkarusTextValue text; -}; - -/// \brief The state of an entity value. -/// \details States provide insight into the nature of a value. -enum IkarusEntityValueState { - /// \brief The value is invalid. - IkarusEntityValueState_Invalid, - /// \brief The value is normal and can be used as-is. - IkarusEntityValueState_Normal, - /// \brief The value is unknown. - IkarusEntityValueState_Indeterminate, -}; +struct IkarusTextValue; /// \brief The value of an entity associated with a property. -struct IkarusEntityValue { - /// \private \brief The type of the value. - IkarusPropertyType _type; - /// \private \brief The data for the value.p - IkarusEntityValueData _data; - /// \private \brief The state of the value. - IkarusEntityValueState _state; -}; +struct IkarusEntityValue; -/// \brief Creates an entity value from a toggle value. +/// \brief Creates a toggle value from a boolean. /// \param value The toggle value. /// \return The entity value. -IKA_API IkarusEntityValue ikarus_value_create_toggle(bool value); -/// \brief Creates an entity value from a number value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value); +/// \brief Creates an indeterminate toggle value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); +/// \brief Sets the value of a toggle value. +/// \param value The toggle value. +/// \pre Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); + +/// \brief Creates a number value from a number. /// \param value The number value. +/// \pre Must be finite & not NaN. /// \return The entity value. -/// \remark If the value is NaN or infinity an InvalidEntityValue is returned. -IKA_API IkarusEntityValue ikarus_value_create_number(long double value); -/// \brief Creates an entity value from a text value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberValue * ikarus_number_value_create(long double value); +/// \brief Creates an indeterminate number value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); +/// \brief Sets the value of a number value. +/// \param value The number value. +/// \pre Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); + +/// \brief Creates a text value from string. /// \param value The text value. +/// \pre Must not be null. /// \return The entity value. -/// \remark If the value is null an InvalidEntityValue is returned. -IKA_API IkarusEntityValue ikarus_value_create_text(char const * value); - -/// \brief Creates an indeterminate entity value of a given type. -/// \param type The type of the value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextValue * ikarus_text_value_create(char const * value); +/// \brief Creates an indeterminate text value. /// \return The entity value. -IKA_API IkarusEntityValue ikarus_value_create_indeterminate(IkarusPropertyType type); +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); +/// \brief Sets the value of a text value. +/// \param value The text value. +/// \pre Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); -/// \brief Fetches the default value for a property type. -/// \remark Not to be confused with the default value of a property. See ikarus_property_get_default_value -/// \param type The property type. -/// \return The default value for the property type. -IKA_API IkarusEntityValue ikarus_value_get_default(IkarusPropertyType type); +/// \brief Checks if a toggle value is indeterminate. +/// \param value The toggle value. +/// \pre Must not be null. +/// \return True if the value is indeterminate, false otherwise. +IKA_API bool ikarus_toggle_value_is_indeterminate(IkarusToggleValue const * value); +/// \brief Checks if a number value is indeterminate. +/// \param value The number value. +/// \pre Must not be null. +/// \return True if the value is indeterminate, false otherwise. +IKA_API bool ikarus_number_value_is_indeterminate(IkarusNumberValue const * value); +/// \brief Checks if a text value is indeterminate. +/// \param value The text value. +/// \pre Must not be null. +/// \return True if the value is indeterminate, false otherwise. +IKA_API bool ikarus_text_value_is_indeterminate(IkarusTextValue const * value); /// \brief Fetches the underlying value of a toggle value. /// \param value The toggle value. /// \return The underlying value. +/// \warning If the value is indeterminate, false is returned. IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value); /// \brief Fetches the underlying value of a number value. /// \param value The number value. /// \return The underlying value. +/// \warning If the value is indeterminate, 0.0 is returned. IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value); /// \brief Fetches the underlying value of a text value. /// \param value The text value. -/// \return The underlying value. +/// \return A copy of the underlying value. +/// \remark The returned value is a copy and owned by the caller. +/// \warning If the value is indeterminate, an empty string is returned. IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); -/// \brief Checks if a toggle value is equal to a boolean. -/// \param value The toggle value. -/// \param check The boolean value. -/// \return False if value is null. True if it is equal to check, false otherwise. -IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * value, bool check); - -/// \brief Checks if a number value is equal to a number. -/// \param value The number value. -/// \param check The number value. -/// \return False if value is null. True if it is equal to check, false otherwise. -IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * value, long double check); - -/// \brief Checks if a text value is equal to a string. -/// \param value The text value. -/// \param check The string value. -/// \return False if value or check are null. True if it is equal to check, false otherwise. -IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * value, char const * check); - -/// \brief Checks if two entity values are equal. -/// \details Two entity values are equal if they are of the same type and their value is considered equal. -/// Note that floating point values can only be checked for approximate equality. -/// \param left The left-hand entity value. -/// \param right The right-hand entity value. -/// \return True if the values are considered equal, false otherwise. -/// \remark Null values compare false to all other values. As do invalid values. Indeterminate values however, compare true to -/// other indeterminate values of the same type. -IKA_API bool ikarus_value_is_equal(IkarusEntityValue const * left, IkarusEntityValue const * right); - -/// \brief Checks if an entity value is invalid. -/// \param value The entity value. -/// \return True if the value is invalid or null, false otherwise. -IKA_API bool ikarus_value_is_invalid(IkarusEntityValue const * value); - -/// \brief Fetches the type of an entity value. -/// \param value The entity value. -/// \return The type of the entity value. -IKA_API IkarusPropertyType ikarus_value_get_type(IkarusEntityValue const * value); - /// \brief Visits an entity value, calling the appropriate function for the value's type. /// \param value The entity value to visit. -/// \param toggle The function to call if the value is a toggle value. -/// \param number The function to call if the value is a number value. -/// \param text The function to call if the value is a text value. -/// \param data The data to pass to the functions. -/// \remark function pointers may be null in which case they are not called. +/// \param toggle The function to call if the value is a toggle value. Skipped if null. +/// \param number The function to call if the value is a number value. Skipped if null. +/// \param text The function to call if the value is a text value. Skipped if null. +/// \param data The data passed to the visitor functions. IKA_API void ikarus_value_visit( - IkarusEntityValue const * value, - void (*toggle)(IkarusToggleValue const *, void *), - void (*number)(IkarusNumberValue const *, void *), - void (*text)(IkarusTextValue const *, void *), + IkarusEntityValue * value, + void (*toggle)(IkarusToggleValue *, void *), + void (*number)(IkarusNumberValue *, void *), + void (*text)(IkarusTextValue *, void *), void * data ); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a0665aa..6b00545 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ file( GLOB_RECURSE FILES + "*.hpp" "*.cpp" ) diff --git a/src/folders/blueprint_folder.hpp b/src/folders/blueprint_folder.hpp new file mode 100644 index 0000000..c98b51b --- /dev/null +++ b/src/folders/blueprint_folder.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct IkarusBlueprintFolder { + IkarusId id; + std::string name_buffer; +}; diff --git a/src/folders/entity_folder.hpp b/src/folders/entity_folder.hpp new file mode 100644 index 0000000..c69e5c7 --- /dev/null +++ b/src/folders/entity_folder.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct IkarusEntityFolder { + IkarusId id; + std::string name_buffer; +}; diff --git a/src/folders/folder.hpp b/src/folders/folder.hpp new file mode 100644 index 0000000..9589a5d --- /dev/null +++ b/src/folders/folder.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +struct IkarusEntityFolder { + std::variant data; +}; diff --git a/src/folders/property_folder.hpp b/src/folders/property_folder.hpp new file mode 100644 index 0000000..11aef0c --- /dev/null +++ b/src/folders/property_folder.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct IkarusPropertyFolder { + IkarusId id; + std::string name_buffer; +}; diff --git a/src/id.cpp b/src/id.cpp index caa08be..b1d28fe 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -4,28 +4,28 @@ #include -IkarusId ikarus_id_from_data(int64_t data) { - return IkarusId{.data = data}; +uint64_t const IKARUS_ID_OBJECT_TYPE_BITS = 8; +uint64_t const IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; + +auto ikarus_id_get_object_type(IkarusId id) -> IkarusObjectType { + return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); } -IkarusObjectType ikarus_id_get_object_type(IkarusId id) { - return static_cast(id.data >> 56); +auto ikarus_id_is_equal(IkarusId left, IkarusId right) -> bool { + return left == right; } -bool ikarus_id_is_equal(IkarusId left, IkarusId right) { - return left.data == right.data; -} - -bool ikarus_id_is_none(IkarusId id) { +auto ikarus_id_is_none(IkarusId id) -> bool { return ikarus_id_is_equal(id, IKARUS_ID_NONE); } -bool ikarus_id_is_unspecified(IkarusId id) { +auto ikarus_id_is_unspecified(IkarusId id) -> bool { return ikarus_id_is_equal(id, IKARUS_ID_UNSPECIFIED); } TEST_CASE("id_object_type", "[id]") { - auto id = ikarus_id_from_data(static_cast(IkarusObjectType_Blueprint) << 56); + // NOLINTNEXTLINE(readability-magic-numbers) + auto id = static_cast(IkarusObjectType_Blueprint) << IKARUS_ID_OBJECT_RANDOM_BITS; REQUIRE(ikarus_id_get_object_type(id) == IkarusObjectType_Blueprint); REQUIRE(!ikarus_id_is_none(id) == IkarusObjectType_Blueprint); @@ -33,9 +33,9 @@ TEST_CASE("id_object_type", "[id]") { } TEST_CASE("id_equal", "[id]") { - auto id = ikarus_id_from_data(static_cast(IkarusObjectType_Blueprint) << 56); + auto id = static_cast(IkarusObjectType_Blueprint) << IKARUS_ID_OBJECT_RANDOM_BITS; auto copy = id; - auto third = ikarus_id_from_data(static_cast(IkarusObjectType_Property) << 56); + auto third = static_cast(IkarusObjectType_Property) << IKARUS_ID_OBJECT_RANDOM_BITS; REQUIRE(ikarus_id_is_equal(id, copy)); REQUIRE(!ikarus_id_is_equal(id, third)); diff --git a/src/id.hpp b/src/id.hpp new file mode 100644 index 0000000..4499c17 --- /dev/null +++ b/src/id.hpp @@ -0,0 +1,40 @@ +// IMPLEMENTATION_DETAIL_DATABASE + +/// \file id.h +/// \author Folling + +/// \privatesection + +#pragma once + +#include +#include +#include + +IKARUS_BEGIN_HEADER + +/// \defgroup id Ids +/// \brief Ids are used to identify objects in the database. +/// \details They are stored as 64 bit integers with the following layout: +/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. +/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. +/// - next 7 bits: #IkarusObjectType +/// - last 56 bits: incremented counter generated by the database +/// @{ + +/// \brief A wrapper around a 64 bit integer that represents the id of an object. +/// \details They are stored as 64 bit integers with the following layout: +/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. +/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. +/// - next 7 bits: #IkarusObjectType +/// - last 56 bits: incremented counter generated by the database +using IkarusId = int64_t; + +/// \brief Fetches the object type of the given id. +/// \param id The id to fetch the object type for. +/// \return The object type of the given id. +IKA_API IkarusObjectType ikarus_id_get_object_type(IkarusId id); + +/// @} + +IKARUS_END_HEADER diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp new file mode 100644 index 0000000..f938b8f --- /dev/null +++ b/src/objects/blueprint.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +/// \private +struct IkarusBlueprint { + IkarusId id; +}; diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp new file mode 100644 index 0000000..a348533 --- /dev/null +++ b/src/objects/entity.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +/// \private +struct IkarusEntity { + IkarusId id; +}; diff --git a/src/objects/object.cpp b/src/objects/object.cpp index 7378c39..fe405b9 100644 --- a/src/objects/object.cpp +++ b/src/objects/object.cpp @@ -1,13 +1,11 @@ #include "ikarus/objects/object.h" -#include - #include -#include +#include -IkarusObjectType ikarus_object_get_type(IkarusObject object) { - return object.type; +IkarusObjectType ikarus_object_get_type(IkarusObject const * object) { + return object->type; } TEST_CASE("object_type", "[object]") { @@ -22,6 +20,6 @@ TEST_CASE("object_type", "[object]") { for (auto type : types) { auto object = IkarusObject{.type = type}; - REQUIRE(ikarus_object_get_type(object) == type); + REQUIRE(ikarus_object_get_type(&object) == type); } } diff --git a/src/objects/object.hpp b/src/objects/object.hpp new file mode 100644 index 0000000..0d696aa --- /dev/null +++ b/src/objects/object.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +struct IkarusObject { + std::variant + data; +}; diff --git a/src/objects/object_type.cpp b/src/objects/object_type.cpp deleted file mode 100644 index a9a3e32..0000000 --- a/src/objects/object_type.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "ikarus/objects/object_type.h" - -#include - -#include - -IkarusObjectType ikarus_folder_type_to_object_type(IkarusFolderType type) { - return static_cast(type); -} - -TEST_CASE("folder_to_object_type_conversion", "[object_type]") { - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_None) == IkarusObjectType_None); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_BlueprintFolder) == IkarusObjectType_BlueprintFolder); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_PropertyFolder) == IkarusObjectType_PropertyFolder); - REQUIRE(ikarus_folder_type_to_object_type(IkarusFolderType_EntityFolder) == IkarusObjectType_EntityFolder); -} diff --git a/src/objects/property.hpp b/src/objects/property.hpp new file mode 100644 index 0000000..f72ad8c --- /dev/null +++ b/src/objects/property.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +/// \private +struct IkarusProperty { + IkarusId id; +}; diff --git a/src/projects/project.hpp b/src/projects/project.hpp new file mode 100644 index 0000000..c805935 --- /dev/null +++ b/src/projects/project.hpp @@ -0,0 +1,10 @@ +#include "ikarus/project/project.h" + +#include +#include + +/// \private +struct IkarusProject { + std::string name; + std::filesystem::path path; +}; diff --git a/src/scopes/blueprint_scope.cpp b/src/scopes/blueprint_scope.cpp new file mode 100644 index 0000000..c1eb0fc --- /dev/null +++ b/src/scopes/blueprint_scope.cpp @@ -0,0 +1,12 @@ +#include "ikarus/scopes/blueprint_scope.h" + +#include +#include + +IkarusBlueprintScope * ikarus_blueprint_scope_create() { + return new IkarusBlueprintScope{}; +} + +struct IkarusObjectScope * ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope) { + return new IkarusObjectScope{.data = *scope}; +} diff --git a/src/scopes/blueprint_scope.hpp b/src/scopes/blueprint_scope.hpp new file mode 100644 index 0000000..ba0bc7d --- /dev/null +++ b/src/scopes/blueprint_scope.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include + +struct IkarusBlueprintScope {}; diff --git a/src/scopes/entity_scope.cpp b/src/scopes/entity_scope.cpp new file mode 100644 index 0000000..a0f78d0 --- /dev/null +++ b/src/scopes/entity_scope.cpp @@ -0,0 +1,12 @@ +#include "ikarus/scopes/entity_scope.h" + +#include +#include + +IkarusEntityScope * ikarus_entity_scope_create() { + return new IkarusEntityScope{}; +} + +IkarusObjectScope * ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope) { + return new IkarusObjectScope{.data = *scope}; +} diff --git a/src/scopes/entity_scope.hpp b/src/scopes/entity_scope.hpp new file mode 100644 index 0000000..6df6550 --- /dev/null +++ b/src/scopes/entity_scope.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include + +struct IkarusEntityScope {}; diff --git a/src/scopes/object_scope.cpp b/src/scopes/object_scope.cpp index 348b007..ddae9a9 100644 --- a/src/scopes/object_scope.cpp +++ b/src/scopes/object_scope.cpp @@ -2,194 +2,81 @@ #include #include +#include #include -#include -#include +#include + #include #include #include -IkarusBlueprintScope ikarus_blueprint_scope_create() { - return IkarusBlueprintScope{._dummy = 0}; -} +#include -IkarusObjectScope ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope scope) { - IkarusObjectScopeData data{}; - data._blueprint = scope; - - return IkarusObjectScope{._type = IkarusObjectScopeType_Blueprint, ._data = data}; -} - -IkarusPropertyScope ikarus_property_scope_create_blueprint(IkarusBlueprint blueprint) { - IkarusPropertyScopeData data{}; - data._blueprint = blueprint; - return IkarusPropertyScope{._type = IkarusPropertyScopeType_Blueprint, ._data = data}; -} - -IkarusPropertyScope ikarus_property_scope_create_entity(IkarusEntity entity) { - IkarusPropertyScopeData data{}; - data._entity = entity; - return IkarusPropertyScope{._type = IkarusPropertyScopeType_Entity, ._data = data}; -} - -IkarusObjectScope ikarus_property_scope_to_object_scope(IkarusPropertyScope scope) { - IkarusObjectScopeData data{}; - data._property = scope; - - return IkarusObjectScope{._type = IkarusObjectScopeType_Property, ._data = data}; -} - -IkarusPropertyScopeType ikarus_property_scope_get_type(IkarusPropertyScope scope) { - return scope._type; -} - -void ikarus_property_scope_visit( - IkarusPropertyScope scope, void(blueprint)(IkarusBlueprint, void *), void(entity)(IkarusEntity, void *), void * data -) { - switch (scope._type) { - case IkarusPropertyScopeType_Blueprint: blueprint(scope._data._blueprint, data); break; - case IkarusPropertyScopeType_Entity: entity(scope._data._entity, data); break; - } -} - -IkarusEntityScope ikarus_entity_scope_create() { - return IkarusEntityScope{._dummy = 0}; -} - -IkarusObjectScope ikarus_entity_scope_to_object_scope(IkarusEntityScope scope) { - IkarusObjectScopeData data{}; - data._entity = scope; - - return IkarusObjectScope{._type = IkarusObjectScopeType_Entity, ._data = data}; -} - -IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope scope) { - return scope._type; +IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope * scope) { + return std::visit( + cppbase::overloaded{ + []([[maybe_unused]] IkarusBlueprintScope const& scope) { return IkarusObjectScopeType_Blueprint; }, + []([[maybe_unused]] IkarusPropertyScope const& scope) { return IkarusObjectScopeType_Property; }, + []([[maybe_unused]] IkarusEntityScope const& scope) { return IkarusObjectScopeType_Entity; }}, + scope->data + ); } void ikarus_object_scope_visit( - IkarusObjectScope scope, - void(blueprint)(IkarusBlueprintScope, void *), - void(property)(IkarusPropertyScope, void *), - void(entity)(IkarusEntityScope, void *), + IkarusObjectScope * scope, + void(blueprint_visitor)(IkarusBlueprintScope *, void *), + void(property_visitor)(IkarusPropertyScope *, void *), + void(entity_visitor)(IkarusEntityScope *, void *), void * data ) { - switch (scope._type) { - case IkarusObjectScopeType_Blueprint: { - if (blueprint != nullptr) { - blueprint(scope._data._blueprint, data); - } - break; - } - case IkarusObjectScopeType_Property: { - if (property != nullptr) { - property(scope._data._property, data); - } - break; - } - case IkarusObjectScopeType_Entity: { - if (entity != nullptr) { - entity(scope._data._entity, data); - } - break; - } - } + std::visit( + cppbase::overloaded{ + [blueprint_visitor, data](IkarusBlueprintScope& scope) { blueprint_visitor(&scope, data); }, + [property_visitor, data](IkarusPropertyScope& scope) { property_visitor(&scope, data); }, + [entity_visitor, data](IkarusEntityScope& scope) { entity_visitor(&scope, data); }}, + scope->data + ); } TEST_CASE("blueprint_object_scope_conversion", "[object_scope]") { - auto blueprint_scope = ikarus_blueprint_scope_create(); - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); - REQUIRE(blueprint_object_scope._type == IkarusObjectScopeType_Blueprint); -} - -TEST_CASE("property_scope_type", "[object_scope]") { - auto blueprint = IkarusBlueprint{}; - auto entity = IkarusEntity{}; - - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); - auto property_entity_scope = ikarus_property_scope_create_entity(entity); - - REQUIRE(ikarus_property_scope_get_type(property_blueprint_scope) == IkarusPropertyScopeType_Blueprint); - REQUIRE(ikarus_property_scope_get_type(property_entity_scope) == IkarusPropertyScopeType_Entity); + auto * blueprint_scope = ikarus_blueprint_scope_create(); + auto * blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); + REQUIRE(ikarus_object_scope_get_type(blueprint_object_scope) == IkarusObjectScopeType_Blueprint); } TEST_CASE("property_object_scope_conversion", "[object_scope]") { - auto blueprint = IkarusBlueprint{}; - auto entity = IkarusEntity{}; + auto * blueprint = ikarus_blueprint_create(); + auto * entity = ikarus_entity_create(); - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); - auto property_blueprint_object_scope = ikarus_property_scope_to_object_scope(property_blueprint_scope); + auto * property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); + auto * property_blueprint_object_scope = ikarus_property_scope_to_object_scope(property_blueprint_scope); - REQUIRE(property_blueprint_object_scope._type == IkarusObjectScopeType_Property); + REQUIRE(ikarus_object_scope_get_type(property_blueprint_object_scope) == IkarusObjectScopeType_Property); - auto property_entity_scope = ikarus_property_scope_create_entity(entity); - auto property_entity_object_scope = ikarus_property_scope_to_object_scope(property_entity_scope); + auto * property_entity_scope = ikarus_property_scope_create_entity(entity); + auto * property_entity_object_scope = ikarus_property_scope_to_object_scope(property_entity_scope); - REQUIRE(property_entity_object_scope._type == IkarusObjectScopeType_Property); -} - -TEST_CASE("property_scope_visiting", "[object_scope]") { - auto blueprint = IkarusBlueprint{}; - auto entity = IkarusEntity{}; - - auto property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); - auto property_entity_scope = ikarus_property_scope_create_entity(entity); - - int test = 0; - - ikarus_property_scope_visit( - property_blueprint_scope, - [](IkarusBlueprint, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusEntity, void * data) { *reinterpret_cast(data) = 2; }, - &test - ); - - REQUIRE(test == 1); - - ikarus_property_scope_visit( - property_entity_scope, - [](IkarusBlueprint, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusEntity, void * data) { *reinterpret_cast(data) = 2; }, - &test - ); - - REQUIRE(test == 2); + REQUIRE(ikarus_object_scope_get_type(property_entity_object_scope) == IkarusObjectScopeType_Property); } TEST_CASE("entity_object_scope_conversion", "[object_scope]") { - auto entity_scope = ikarus_entity_scope_create(); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); - REQUIRE(entity_object_scope._type == IkarusObjectScopeType_Entity); -} - -TEST_CASE("object_scope_type_fetching", "[object_scope]") { - auto blueprint = IkarusBlueprint{}; - - auto blueprint_scope = ikarus_blueprint_scope_create(); - auto property_scope = ikarus_property_scope_create_blueprint(blueprint); - auto entity_scope = ikarus_entity_scope_create(); - - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); - auto property_object_scope = ikarus_property_scope_to_object_scope(property_scope); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); - - REQUIRE(ikarus_object_scope_get_type(blueprint_object_scope) == IkarusObjectScopeType_Blueprint); - REQUIRE(ikarus_object_scope_get_type(property_object_scope) == IkarusObjectScopeType_Property); + auto * entity_scope = ikarus_entity_scope_create(); + auto * entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); REQUIRE(ikarus_object_scope_get_type(entity_object_scope) == IkarusObjectScopeType_Entity); } TEST_CASE("object_scope_visiting", "[object_scope]") { - auto blueprint = IkarusBlueprint{}; + auto * blueprint = ikarus_blueprint_create(); - auto blueprint_scope = ikarus_blueprint_scope_create(); - auto property_scope = ikarus_property_scope_create_blueprint(blueprint); - auto entity_scope = ikarus_entity_scope_create(); + auto * blueprint_scope = ikarus_blueprint_scope_create(); + auto * property_scope = ikarus_property_scope_create_blueprint(blueprint); + auto * entity_scope = ikarus_entity_scope_create(); - auto blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); - auto property_object_scope = ikarus_property_scope_to_object_scope(property_scope); - auto entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); + auto * blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); + auto * property_object_scope = ikarus_property_scope_to_object_scope(property_scope); + auto * entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); auto scopes = { std::make_pair(blueprint_object_scope, 1), @@ -202,9 +89,9 @@ TEST_CASE("object_scope_visiting", "[object_scope]") { ikarus_object_scope_visit( scope, - [](IkarusBlueprintScope, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusPropertyScope, void * data) { *reinterpret_cast(data) = 2; }, - [](IkarusEntityScope, void * data) { *reinterpret_cast(data) = 3; }, + [](IkarusBlueprintScope *, void * data) { *static_cast(data) = 1; }, + [](IkarusPropertyScope *, void * data) { *static_cast(data) = 2; }, + [](IkarusEntityScope *, void * data) { *static_cast(data) = 3; }, &test ); diff --git a/src/scopes/object_scope.hpp b/src/scopes/object_scope.hpp new file mode 100644 index 0000000..9bc3b09 --- /dev/null +++ b/src/scopes/object_scope.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include +#include +#include + +struct IkarusObjectScope { + std::variant data; +}; diff --git a/src/scopes/property_scope.cpp b/src/scopes/property_scope.cpp new file mode 100644 index 0000000..26f45f5 --- /dev/null +++ b/src/scopes/property_scope.cpp @@ -0,0 +1,35 @@ +#include "ikarus/scopes/property_scope.h" + +#include + +#include +#include +#include +#include + +IkarusPropertyScope * ikarus_property_scope_create_blueprint(IkarusBlueprint * blueprint) { + return new IkarusPropertyScope{.data = *blueprint}; +} + +IkarusPropertyScope * ikarus_property_scope_create_entity(IkarusEntity * entity) { + return new IkarusPropertyScope{.data = *entity}; +} + +IkarusObjectScope * ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope) { + return new IkarusObjectScope{.data = *scope}; +} + +void ikarus_property_scope_visit( + IkarusPropertyScope * scope, + void (*blueprint_func)(IkarusBlueprint *, void *), + void (*entity_func)(IkarusEntity *, void *), + void * data +) { + std::visit( + cppbase::overloaded{ + [blueprint_func, data](IkarusBlueprint& blueprint) { blueprint_func(&blueprint, data); }, + [entity_func, data](IkarusEntity& entity) { entity_func(&entity, data); }, + }, + scope->data + ); +} diff --git a/src/scopes/property_scope.hpp b/src/scopes/property_scope.hpp new file mode 100644 index 0000000..4d3aad0 --- /dev/null +++ b/src/scopes/property_scope.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include + +#include +#include + +struct IkarusPropertyScope { + std::variant data; +}; diff --git a/src/values/value.cpp b/src/values/value.cpp index 56fa9c5..a5d6457 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -25,7 +25,7 @@ IKA_API IkarusEntityValue value_create_invalid(IkarusPropertyType type) { IkarusEntityValue ikarus_value_create_toggle(bool value) { return IkarusEntityValue{ ._type = IkarusPropertyType_Toggle, - ._data = IkarusEntityValueData{.toggle = IkarusToggleValue{._value = value}}, + ._data = IkarusEntityValueData{._toggle = IkarusToggleValue{._value = value}}, ._state = IkarusEntityValueState_Normal, }; } @@ -37,7 +37,7 @@ IkarusEntityValue ikarus_value_create_number(long double value) { return IkarusEntityValue{ ._type = IkarusPropertyType_Number, - ._data = IkarusEntityValueData{.number = IkarusNumberValue{._value = value}}, + ._data = IkarusEntityValueData{._number = IkarusNumberValue{._value = value}}, ._state = IkarusEntityValueState_Normal, }; } @@ -49,7 +49,7 @@ IkarusEntityValue ikarus_value_create_text(char const * value) { return IkarusEntityValue{ ._type = IkarusPropertyType_Text, - ._data = IkarusEntityValueData{.text = IkarusTextValue{._value = value}}, + ._data = IkarusEntityValueData{._text = IkarusTextValue{._value = value}}, ._state = IkarusEntityValueState_Normal, }; } @@ -59,15 +59,15 @@ IkarusEntityValue ikarus_value_create_indeterminate(IkarusPropertyType type) { switch (type) { case IkarusPropertyType_Toggle: { - data.toggle = IkarusToggleValue{._value = false}; + data._toggle = IkarusToggleValue{._value = false}; break; } case IkarusPropertyType_Number: { - data.number = IkarusNumberValue{._value = 0.0}; + data._number = IkarusNumberValue{._value = 0.0}; break; } case IkarusPropertyType_Text: { - data.text = IkarusTextValue{._value = ""}; + data._text = IkarusTextValue{._value = ""}; break; } default: return value_create_invalid(type); @@ -133,9 +133,9 @@ bool ikarus_value_is_equal(IkarusEntityValue const * left, IkarusEntityValue con } switch (left->_type) { - case IkarusPropertyType_Toggle: return left->_data.toggle._value == right->_data.toggle._value; - case IkarusPropertyType_Number: return left->_data.number._value == right->_data.number._value; - case IkarusPropertyType_Text: return std::strcmp(left->_data.text._value, right->_data.text._value) == 0; + case IkarusPropertyType_Toggle: return left->_data._toggle._value == right->_data._toggle._value; + case IkarusPropertyType_Number: return left->_data._number._value == right->_data._number._value; + case IkarusPropertyType_Text: return std::strcmp(left->_data._text._value, right->_data._text._value) == 0; default: return false; } } @@ -162,19 +162,19 @@ void ikarus_value_visit( switch (value->_type) { case IkarusPropertyType_Toggle: { if (toggle != nullptr) { - toggle(&value->_data.toggle, data); + toggle(&value->_data._toggle, data); } break; } case IkarusPropertyType_Number: { if (number != nullptr) { - number(&value->_data.number, data); + number(&value->_data._number, data); } break; } case IkarusPropertyType_Text: { if (text != nullptr) { - text(&value->_data.text, data); + text(&value->_data._text, data); } break; } @@ -186,14 +186,14 @@ TEST_CASE("toggle_value_creation", "[value]") { auto toggle_value = ikarus_value_create_toggle(true); REQUIRE(ikarus_value_get_type(&toggle_value) == IkarusPropertyType_Toggle); - REQUIRE(ikarus_toggle_value_is_equal(&toggle_value._data.toggle, true)); + REQUIRE(ikarus_toggle_value_is_equal(&toggle_value._data._toggle, true)); } TEST_CASE("number_value_creation", "[value]") { auto number_value = ikarus_value_create_number(1.0); REQUIRE(ikarus_value_get_type(&number_value) == IkarusPropertyType_Number); - REQUIRE(ikarus_number_value_is_equal(&number_value._data.number, 1.0)); + REQUIRE(ikarus_number_value_is_equal(&number_value._data._number, 1.0)); auto nan_value = ikarus_value_create_number(std::numeric_limits::quiet_NaN()); REQUIRE(ikarus_value_is_invalid(&nan_value)); @@ -209,7 +209,7 @@ TEST_CASE("text_value_creation", "[value]") { auto text_value = ikarus_value_create_text("test"); REQUIRE(ikarus_value_get_type(&text_value) == IkarusPropertyType_Text); - REQUIRE(ikarus_text_value_is_equal(&text_value._data.text, "test")); + REQUIRE(ikarus_text_value_is_equal(&text_value._data._text, "test")); auto null_value = ikarus_value_create_text(nullptr); REQUIRE(ikarus_value_is_invalid(&null_value)); @@ -232,8 +232,8 @@ TEST_CASE("toggle_value_underlying", "[value]") { auto true_toggle_value = ikarus_value_create_toggle(true); auto false_toggle_value = ikarus_value_create_toggle(false); - REQUIRE(ikarus_toggle_value_get_underlying(&true_toggle_value._data.toggle) == true); - REQUIRE(ikarus_toggle_value_get_underlying(&false_toggle_value._data.toggle) == false); + REQUIRE(ikarus_toggle_value_get_underlying(&true_toggle_value._data._toggle) == true); + REQUIRE(ikarus_toggle_value_get_underlying(&false_toggle_value._data._toggle) == false); } TEST_CASE("number_value_underlying", "[value]") { @@ -241,25 +241,25 @@ TEST_CASE("number_value_underlying", "[value]") { auto third_number_value = ikarus_value_create_number(1.0 / 3.0); auto large_number_value = ikarus_value_create_number(1.2345678910e123); - REQUIRE(ikarus_number_value_get_underlying(&zero_number_value._data.number) == 0.0); - REQUIRE(ikarus_number_value_get_underlying(&third_number_value._data.number) == 1.0 / 3.0); - REQUIRE(ikarus_number_value_get_underlying(&large_number_value._data.number) == 1.2345678910e123); + REQUIRE(ikarus_number_value_get_underlying(&zero_number_value._data._number) == 0.0); + REQUIRE(ikarus_number_value_get_underlying(&third_number_value._data._number) == 1.0 / 3.0); + REQUIRE(ikarus_number_value_get_underlying(&large_number_value._data._number) == 1.2345678910e123); } TEST_CASE("text_value_underlying", "[value]") { auto test_value = ikarus_value_create_text("test"); auto empty_value = ikarus_value_create_text(""); - REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&test_value._data.text), "test") == 0); - REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&empty_value._data.text), "") == 0); + REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&test_value._data._text), "test") == 0); + REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&empty_value._data._text), "") == 0); } TEST_CASE("toggle_comparison", "[value]") { auto true_toggle_value = ikarus_value_create_toggle(true); auto false_toggle_value = ikarus_value_create_toggle(false); - REQUIRE(ikarus_toggle_value_is_equal(&true_toggle_value._data.toggle, true)); - REQUIRE(ikarus_toggle_value_is_equal(&false_toggle_value._data.toggle, false)); + REQUIRE(ikarus_toggle_value_is_equal(&true_toggle_value._data._toggle, true)); + REQUIRE(ikarus_toggle_value_is_equal(&false_toggle_value._data._toggle, false)); } TEST_CASE("number_comparison", "[value]") { @@ -267,17 +267,17 @@ TEST_CASE("number_comparison", "[value]") { auto third_number_value = ikarus_value_create_number(1.0 / 3.0); auto large_number_value = ikarus_value_create_number(1.2345678910e123); - REQUIRE(ikarus_number_value_is_equal(&zero_number_value._data.number, 0.0)); - REQUIRE(ikarus_number_value_is_equal(&third_number_value._data.number, 1.0 / 6.0 + 1.0 / 6.0)); - REQUIRE(ikarus_number_value_is_equal(&large_number_value._data.number, 1.2345678910e123)); + REQUIRE(ikarus_number_value_is_equal(&zero_number_value._data._number, 0.0)); + REQUIRE(ikarus_number_value_is_equal(&third_number_value._data._number, 1.0 / 6.0 + 1.0 / 6.0)); + REQUIRE(ikarus_number_value_is_equal(&large_number_value._data._number, 1.2345678910e123)); } TEST_CASE("text_comparison", "[value]") { auto test_value = ikarus_value_create_text("test"); auto empty_value = ikarus_value_create_text(""); - REQUIRE(ikarus_text_value_is_equal(&test_value._data.text, "test")); - REQUIRE(ikarus_text_value_is_equal(&empty_value._data.text, "")); + REQUIRE(ikarus_text_value_is_equal(&test_value._data._text, "test")); + REQUIRE(ikarus_text_value_is_equal(&empty_value._data._text, "")); } TEST_CASE("value_comparison", "[value]") { diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 416e326..a397a5e 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 416e326c60f3763a43dec8c66e58616ab50d1a2a +Subproject commit a397a5e8c35c7b30ecac8b4994301a585e24560b From 6d9a70e03db6444c9fa34aeae6c6e6c3b3d950d2 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 14 Nov 2023 16:26:29 +0100 Subject: [PATCH 091/166] intermediate commit Signed-off-by: Folling --- include/ikarus/folders/blueprint_folder.h | 151 -------- include/ikarus/folders/blueprint_tree_item.h | 31 -- include/ikarus/folders/entity_folder.h | 151 -------- include/ikarus/folders/entity_tree_item.h | 31 -- include/ikarus/folders/folder.h | 38 -- include/ikarus/folders/folder_type.h | 26 -- include/ikarus/folders/property_folder.h | 159 -------- include/ikarus/folders/property_tree_item.h | 31 -- include/ikarus/objects/blueprint.h | 78 +--- include/ikarus/objects/entity.h | 7 +- include/ikarus/objects/property.h | 61 +--- .../ikarus/{project => persistence}/project.h | 0 include/ikarus/scopes/blueprint_scope.h | 30 -- include/ikarus/scopes/entity_scope.h | 29 -- include/ikarus/scopes/object_scope.h | 46 --- include/ikarus/scopes/property_scope.h | 48 --- src/folders/blueprint_folder.hpp | 8 - src/folders/entity_folder.hpp | 8 - src/folders/folder.hpp | 13 - src/folders/property_folder.hpp | 8 - src/id.cpp | 26 +- src/objects/blueprint.hpp | 3 +- src/objects/entity.hpp | 3 +- src/objects/object.cpp | 25 -- src/objects/object.hpp | 10 +- src/objects/property.hpp | 3 +- .../migrations/m1_initial_layout.sql | 111 ++++++ src/{projects => persistence}/project.hpp | 5 +- src/scopes/blueprint_scope.cpp | 12 - src/scopes/blueprint_scope.hpp | 5 - src/scopes/entity_scope.cpp | 12 - src/scopes/entity_scope.hpp | 5 - src/scopes/object_scope.cpp | 100 ----- src/scopes/object_scope.hpp | 11 - src/scopes/property_scope.cpp | 35 -- src/scopes/property_scope.hpp | 12 - src/values/value.cpp | 343 ------------------ 37 files changed, 131 insertions(+), 1544 deletions(-) delete mode 100644 include/ikarus/folders/blueprint_folder.h delete mode 100644 include/ikarus/folders/blueprint_tree_item.h delete mode 100644 include/ikarus/folders/entity_folder.h delete mode 100644 include/ikarus/folders/entity_tree_item.h delete mode 100644 include/ikarus/folders/folder.h delete mode 100644 include/ikarus/folders/folder_type.h delete mode 100644 include/ikarus/folders/property_folder.h delete mode 100644 include/ikarus/folders/property_tree_item.h rename include/ikarus/{project => persistence}/project.h (100%) delete mode 100644 include/ikarus/scopes/blueprint_scope.h delete mode 100644 include/ikarus/scopes/entity_scope.h delete mode 100644 include/ikarus/scopes/object_scope.h delete mode 100644 include/ikarus/scopes/property_scope.h delete mode 100644 src/folders/blueprint_folder.hpp delete mode 100644 src/folders/entity_folder.hpp delete mode 100644 src/folders/folder.hpp delete mode 100644 src/folders/property_folder.hpp delete mode 100644 src/objects/object.cpp create mode 100644 src/persistence/migrations/m1_initial_layout.sql rename src/{projects => persistence}/project.hpp (61%) delete mode 100644 src/scopes/blueprint_scope.cpp delete mode 100644 src/scopes/blueprint_scope.hpp delete mode 100644 src/scopes/entity_scope.cpp delete mode 100644 src/scopes/entity_scope.hpp delete mode 100644 src/scopes/object_scope.cpp delete mode 100644 src/scopes/object_scope.hpp delete mode 100644 src/scopes/property_scope.cpp delete mode 100644 src/scopes/property_scope.hpp delete mode 100644 src/values/value.cpp diff --git a/include/ikarus/folders/blueprint_folder.h b/include/ikarus/folders/blueprint_folder.h deleted file mode 100644 index 430c147..0000000 --- a/include/ikarus/folders/blueprint_folder.h +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once - -/// \file blueprint_folder.h -/// \author Folling - -#include - -/// \addtogroup blueprints Blueprints -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief A blueprint folder, storing blueprints and other blueprint folders. -struct IkarusBlueprintFolder; - -/// \brief Creates a blueprint folder. -/// \param project The project the blueprint folder is part of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the blueprint folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created blueprint folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprintFolder * ikarus_blueprint_folder_create( - struct IkarusProject * project, IkarusBlueprintFolder * parent, size_t position, char const * name -); - -/// \brief Copies a blueprint folder. -/// \details Creates a copy of the blueprint folder without its children. -/// \param blueprint_folder The blueprint folder to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the blueprint folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created blueprint folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprintFolder * ikarus_blueprint_folder_copy( - IkarusBlueprintFolder * blueprint_folder, IkarusBlueprintFolder * parent, size_t position, char const * name -); - -/// \brief Deletes a blueprint folder and all its children -/// \param blueprint_folder The blueprint folder to delete. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param keep_children If true, the children of the blueprint folder will be moved to the parent folder. -IKA_API void ikarus_blueprint_folder_delete(IkarusBlueprintFolder * blueprint_folder, bool keep_children); - -/// \brief Gets the project of a blueprint folder. -/// \param blueprint_folder The blueprint folder to get the project of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The project of the blueprint folder or null if an error occurs. -IKA_API struct IkarusProject * ikarus_blueprint_folder_get_project(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Gets the parent folder of a blueprint folder. -/// \param blueprint_folder The blueprint folder to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the blueprint folder or null if an error occurs. -IKA_API struct IkarusBlueprintFolder * ikarus_blueprint_folder_get_parent(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Gets the position of a blueprint folder within its parent folder. -/// \param blueprint_folder The blueprint folder to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the blueprint folder or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_folder_get_position(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Gets the name of a blueprint folder. -/// \param blueprint_folder The blueprint folder to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the blueprint folder or null if an error occurs. -/// \remark The returned pointer is valid until the blueprint folder is freed but may be invalidated by other operations. -IKA_API char const * ikarus_blueprint_folder_get_name(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Gets the number of children of a blueprint folder. -/// \param blueprint_folder The blueprint folder to get the number of children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of children or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_folder_get_child_count(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Gets the children of a blueprint folder. -/// \param blueprint_folder The blueprint folder to get the children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param children_out The buffer to write the children to. -/// \pre \li Must not be null. -/// \param children_out_size The size of the buffer. -IKA_API void ikarus_blueprint_folder_get_children( - IkarusBlueprintFolder const * blueprint_folder, struct IkarusBlueprintTreeItem ** children_out, size_t children_out_size -); - -/// \brief Sets the parent folder of an blueprint folder. -/// \param blueprint_folder The blueprint folder to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the blueprint folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_blueprint_folder_set_parent( - IkarusBlueprintFolder * blueprint_folder, struct IkarusEntityFolder * new_parent, size_t new_position -); - -/// \brief Sets the position of an blueprint folder within its parent folder. -/// \param blueprint_folder The blueprint folder to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the blueprint folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_blueprint_folder_set_position(IkarusBlueprintFolder * blueprint_folder, size_t new_position); - -/// \brief Sets the name of an blueprint folder. -/// \param blueprint_folder The blueprint folder to set the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_name The new name of the blueprint folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_blueprint_folder_set_name(IkarusBlueprintFolder * blueprint_folder, char const * new_name); - -/// \brief Converts a blueprint folder to a generic folder. -/// \param blueprint_folder The blueprint folder to convert. -/// \return The constructed folder, representing the blueprint folder. -IKA_API struct IkarusFolder * ikarus_blueprint_folder_to_folder(IkarusBlueprintFolder const * blueprint_folder); - -/// \brief Converts a blueprint folder to an object. -/// \param blueprint_folder The blueprint folder to convert. -/// \return The constructed object, representing the blueprint folder. -IKA_API struct IkarusObject * ikarus_blueprint_folder_to_object(IkarusBlueprintFolder const * blueprint_folder); - -IKARUS_END_HEADER - -// @} diff --git a/include/ikarus/folders/blueprint_tree_item.h b/include/ikarus/folders/blueprint_tree_item.h deleted file mode 100644 index 5a09dcc..0000000 --- a/include/ikarus/folders/blueprint_tree_item.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file blueprint_tree_item.h -/// \author Folling - -#include - -/// \addtogroup blueprints Blueprints -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusBlueprintTreeItem; - -/// \brief Visits a blueprint tree item, calling the appropriate visitor function. -/// \param item The item to visit. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param blueprint_visitor The visitor function called if the item is a blueprint. Skipped if null. -/// \param blueprint_folder_visitor The visitor function called if the item is a blueprint folder. Skipped if null. -/// \param data The data passed to the visitor functions. -IKA_API void ikarus_blueprint_tree_item_visit( - struct IkarusBlueprintTreeItem * item, - void (*blueprint_visitor)(struct IkarusBlueprint * blueprint, void * data), - void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder * folder, void * data), - void * data -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/folders/entity_folder.h b/include/ikarus/folders/entity_folder.h deleted file mode 100644 index 8c516cb..0000000 --- a/include/ikarus/folders/entity_folder.h +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once - -/// \file entity_folder.h -/// \author Folling - -#include - -/// \addtogroup entities Entities -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief A entity folder, storing entities and other entity folders. -struct IkarusEntityFolder; - -/// \brief Creates a entity folder. -/// \param project The project the entity folder is part of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the entity folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created entity folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusEntityFolder * ikarus_entity_folder_create( - struct IkarusProject * project, IkarusEntityFolder * parent, size_t position, char const * name -); - -/// \brief Copies a entity folder. -/// \details Creates a copy of the entity folder without its children. -/// \param entity_folder The entity folder to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the entity folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created entity folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusEntityFolder * ikarus_entity_folder_copy( - IkarusEntityFolder * entity_folder, IkarusEntityFolder * parent, size_t position, char const * name -); - -/// \brief Deletes a entity folder and all its children -/// \param entity_folder The entity folder to delete. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param keep_children If true, the children of the entity folder will be moved to the parent folder. -IKA_API void ikarus_entity_folder_delete(IkarusEntityFolder * entity_folder, bool keep_children); - -/// \brief Gets the project of a entity folder. -/// \param entity_folder The entity folder to get the project of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The project of the entity folder or null if an error occurs. -IKA_API struct IkarusProject * ikarus_entity_folder_get_project(IkarusEntityFolder const * entity_folder); - -/// \brief Gets the parent folder of a entity folder. -/// \param entity_folder The entity folder to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the entity folder or null if an error occurs. -IKA_API struct IkarusEntityFolder * ikarus_entity_folder_get_parent(IkarusEntityFolder const * entity_folder); - -/// \brief Gets the position of a entity folder within its parent folder. -/// \param entity_folder The entity folder to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the entity folder or undefined if an error occurs. -IKA_API size_t ikarus_entity_folder_get_position(IkarusEntityFolder const * entity_folder); - -/// \brief Gets the name of a entity folder. -/// \param entity_folder The entity folder to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the entity folder or null if an error occurs. -/// \remark The returned pointer is valid until the entity folder is freed but may be invalidated by other operations. -IKA_API char const * ikarus_entity_folder_get_name(IkarusEntityFolder const * entity_folder); - -/// \brief Gets the number of children of a entity folder. -/// \param entity_folder The entity folder to get the number of children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of children or undefined if an error occurs. -IKA_API size_t ikarus_entity_folder_get_child_count(IkarusEntityFolder const * entity_folder); - -/// \brief Gets the children of a entity folder. -/// \param entity_folder The entity folder to get the children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param children_out The buffer to write the children to. -/// \pre \li Must not be null. -/// \param children_out_size The size of the buffer. -IKA_API void ikarus_entity_folder_get_children( - IkarusEntityFolder const * entity_folder, struct IkarusEntityTreeItem ** children_out, size_t children_out_size -); - -/// \brief Sets the parent folder of an entity folder. -/// \param entity_folder The entity folder to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the entity folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_entity_folder_set_parent( - IkarusEntityFolder * entity_folder, struct IkarusEntityFolder * new_parent, size_t new_position -); - -/// \brief Sets the position of an entity folder within its parent folder. -/// \param entity_folder The entity folder to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the entity folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_entity_folder_set_position(IkarusEntityFolder * entity_folder, size_t new_position); - -/// \brief Sets the name of an entity folder. -/// \param entity_folder The entity folder to set the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_name The new name of the entity folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_entity_folder_set_name(IkarusEntityFolder * entity_folder, char const * new_name); - -/// \brief Converts a entity folder to a generic folder. -/// \param entity_folder The entity folder to convert. -/// \return The constructed folder, representing the entity folder. -IKA_API struct IkarusFolder * ikarus_entity_folder_to_folder(IkarusEntityFolder const * entity_folder); - -/// \brief Converts a entity folder to an object. -/// \param entity_folder The entity folder to convert. -/// \return The constructed object, representing the entity folder. -IKA_API struct IkarusObject * ikarus_entity_folder_to_object(IkarusEntityFolder const * entity_folder); - -IKARUS_END_HEADER - -// @} diff --git a/include/ikarus/folders/entity_tree_item.h b/include/ikarus/folders/entity_tree_item.h deleted file mode 100644 index 516455f..0000000 --- a/include/ikarus/folders/entity_tree_item.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file entity_tree_item.h -/// \author Folling - -#include - -/// \addtogroup entities Entities -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusEntityTreeItem; - -/// \brief Visits a entity tree item, calling the appropriate visitor function. -/// \param item The item to visit. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param entity_visitor The visitor function called if the item is a entity. Skipped if null. -/// \param entity_folder_visitor The visitor function called if the item is a entity folder. Skipped if null. -/// \param data The data passed to the visitor functions. -IKA_API void ikarus_entity_tree_item_visit( - struct IkarusEntityTreeItem * item, - void (*entity_visitor)(struct IkarusEntity * entity, void * data), - void (*entity_folder_visitor)(struct IkarusEntityFolder * folder, void * data), - void * data -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/folders/folder.h b/include/ikarus/folders/folder.h deleted file mode 100644 index d0d60e2..0000000 --- a/include/ikarus/folders/folder.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -/// \file folder.h -/// \author Folling - -#include -#include -#include -#include -#include - -IKARUS_BEGIN_HEADER - -/// \defgroup folder Folders -/// \brief Folders are used to group objects together. -/// @{ - -/// \brief A generic folder. Similar to how Objects wrap all types of objects, Folders wrap all types of folders. -struct IkarusFolder; - -/// \brief Special value for inserting objects at the end of a folder. -enum FolderPosition { FolderPosition_EndOfFolder = -1 }; - -/// \brief Visits a folder. Calling the appropriate function for the folder's type. -/// \param folder The folder to visit. -/// \param blueprint_visitor The function to call if the folder is a blueprint folder. -/// \param property_visitor The function to call if the folder is a property folder. -/// \param entity_visitor The function to call if the folder is an entity folder. -/// \param data The data passed to the visitor functions. -IKA_API void ikarus_folder_visit( - IkarusFolder * folder, - void (*blueprint_visitor)(IkarusBlueprintFolder *, void *), - void (*property_visitor)(IkarusPropertyFolder *, void *), - void (*entity_visitor)(IkarusEntityFolder *, void *), - void * data -); - -IKARUS_END_HEADER diff --git a/include/ikarus/folders/folder_type.h b/include/ikarus/folders/folder_type.h deleted file mode 100644 index d3ac05e..0000000 --- a/include/ikarus/folders/folder_type.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -// IMPLEMENTATION_DETAIL_FOLDER_TYPES - -/// \file folder_type.h -/// \author Folling - -#include - -/// \addtogroup folder folders -/// @{ - -/// \brief The type of an folder. -/// \remark The values are identical to their counterparts in #IkarusObjectType. -enum IkarusFolderType { - /// \brief Not a folder or no folder. - IkarusFolderType_None = 0, - /// \brief An IkarusBlueprintFolder - IkarusFolderType_BlueprintFolder = 0b0100'0001, - /// \brief An IkarusPropertyFolder - IkarusFolderType_PropertyFolder = 0b0100'0010, - /// \brief An IkarusEntityFolder - IkarusFolderType_EntityFolder = 0b0100'0011, -}; - -/// @} diff --git a/include/ikarus/folders/property_folder.h b/include/ikarus/folders/property_folder.h deleted file mode 100644 index d55d6cf..0000000 --- a/include/ikarus/folders/property_folder.h +++ /dev/null @@ -1,159 +0,0 @@ -#pragma once - -/// \file property_folder.h -/// \author Folling - -#include - -/// \addtogroup properties Properties -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief A property folder, storing properties and other property folders. -struct IkarusPropertyFolder; - -/// \brief Creates a property folder. -/// \param project The project the property folder is part of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the property folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created property folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusPropertyFolder * ikarus_property_folder_create( - struct IkarusProject * project, IkarusPropertyFolder * parent, size_t position, char const * name -); - -/// \brief Copies a property folder. -/// \details Creates a copy of the property folder without its children. -/// \param property_folder The property folder to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the property folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created property folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusPropertyFolder * ikarus_property_folder_copy( - IkarusPropertyFolder * property_folder, IkarusPropertyFolder * parent, size_t position, char const * name -); - -/// \brief Deletes a property folder and all its children -/// \param property_folder The property folder to delete. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param keep_children If true, the children of the property folder will be moved to the parent folder. -IKA_API void ikarus_property_folder_delete(IkarusPropertyFolder * property_folder, bool keep_children); - -/// \brief Gets the project of a property folder. -/// \param property_folder The property folder to get the project of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The project of the property folder or null if an error occurs. -IKA_API struct IkarusProject * ikarus_property_folder_get_project(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the property source of a property folder. -/// \param property_folder The property folder to get the property source of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The property source of the property folder or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * ikarus_property_folder_get_source(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the parent folder of a property folder. -/// \param property_folder The property folder to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the property folder or null if an error occurs. -IKA_API struct IkarusPropertyFolder * ikarus_property_folder_get_parent(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the position of a property folder within its parent folder. -/// \param property_folder The property folder to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the property folder or undefined if an error occurs. -IKA_API size_t ikarus_property_folder_get_position(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the name of a property folder. -/// \param property_folder The property folder to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the property folder or null if an error occurs. -/// \remark The returned pointer is valid until the property folder is freed but may be invalidated by other operations. -IKA_API char const * ikarus_property_folder_get_name(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the number of children of a property folder. -/// \param property_folder The property folder to get the number of children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of children or undefined if an error occurs. -IKA_API size_t ikarus_property_folder_get_child_count(IkarusPropertyFolder const * property_folder); - -/// \brief Gets the children of a property folder. -/// \param property_folder The property folder to get the children of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param children_out The buffer to write the children to. -/// \pre \li Must not be null. -/// \param children_out_size The size of the buffer. -IKA_API void ikarus_property_folder_get_children( - IkarusPropertyFolder const * property_folder, struct IkarusPropertyTreeItem ** children_out, size_t children_out_size -); - -/// \brief Sets the parent folder of an property folder. -/// \param property_folder The property folder to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the property folder in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_property_folder_set_parent( - IkarusPropertyFolder * property_folder, struct IkarusPropertyFolder * new_parent, size_t new_position -); - -/// \brief Sets the position of an property folder within its parent folder. -/// \param property_folder The property folder to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the property folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_property_folder_set_position(IkarusPropertyFolder * property_folder, size_t new_position); - -/// \brief Sets the name of an property folder. -/// \param property_folder The property folder to set the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_name The new name of the property folder. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_property_folder_set_name(IkarusPropertyFolder * property_folder, char const * new_name); - -/// \brief Converts a property folder to a generic folder. -/// \param property_folder The property folder to convert. -/// \return The constructed folder, representing the property folder. -IKA_API struct IkarusFolder * ikarus_property_folder_to_folder(IkarusPropertyFolder const * property_folder); - -/// \brief Converts a property folder to an object. -/// \param property_folder The property folder to convert. -/// \return The constructed object, representing the property folder. -IKA_API struct IkarusObject * ikarus_property_folder_to_object(IkarusPropertyFolder const * property_folder); - -IKARUS_END_HEADER - -// @} diff --git a/include/ikarus/folders/property_tree_item.h b/include/ikarus/folders/property_tree_item.h deleted file mode 100644 index 0b37c2c..0000000 --- a/include/ikarus/folders/property_tree_item.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file property_tree_item.h -/// \author Folling - -#include - -/// \addtogroup properties Properties -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusPropertyTreeItem; - -/// \brief Visits a property tree item, calling the appropriate visitor function. -/// \param item The item to visit. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param property_visitor The visitor function called if the item is a property. Skipped if null. -/// \param property_folder_visitor The visitor function called if the item is a property folder. Skipped if null. -/// \param data The data passed to the visitor functions. -IKA_API void ikarus_property_tree_item_visit( - struct IkarusPropertyTreeItem * item, - void (*property_visitor)(struct IkarusProperty * property, void * data), - void (*property_folder_visitor)(struct IkarusPropertyFolder * folder, void * data), - void * data -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index e47b1df..ed35c89 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -21,37 +21,25 @@ struct IkarusBlueprint; /// \param project The project the blueprint is part of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param parent The parent folder of the blueprint. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the blueprint in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the blueprint. /// \pre \li Must not be null. /// \pre \li Must not be empty. /// \return The created blueprint or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprint * ikarus_blueprint_create( - struct IkarusProject * project, struct IkarusBlueprintFolder * parent, size_t position, char const * name -); +IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name); /// \brief Creates a blueprint from an entity. /// \details The created blueprint will have the same properties as the entity. /// \param entity The entity to create the blueprint from. /// \pre \li Must not be null. /// \param link_entity If true, the entity will be linked to the blueprint. If not they will remain separate. -/// \param parent The parent folder of the blueprint. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the blueprint in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the blueprint. Must not be empty. /// \pre \li Must not be null. /// \pre \li Must not be empty. /// \return The created blueprint or null if an error occurs. /// \remark Must be freed using #ikarus_free. IKA_API IkarusBlueprint * ikarus_blueprint_create_from_entity( - struct IkarusEntity * entity, bool link_entity, struct IkarusBlueprintFolder * parent, size_t position, char const * name + struct IkarusEntity * entity, bool link_entity, char const * name ); /// \brief Copies a blueprint. @@ -59,20 +47,13 @@ IKA_API IkarusBlueprint * ikarus_blueprint_create_from_entity( /// \param blueprint The blueprint to copy. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param parent The parent folder of the blueprint. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the blueprint in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the blueprint. /// \pre \li Must not be null. /// \pre \li Must not be empty. /// \return The created blueprint or null if an error occurs. /// \remark Linked entities won't be copied. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprint * ikarus_blueprint_copy( - IkarusBlueprint const * blueprint, struct IkarusBlueprintFolder * parent, size_t position, char const * name -); +IKA_API IkarusBlueprint * ikarus_blueprint_copy(IkarusBlueprint const * blueprint, char const * name); /// \brief Deletes a blueprint. /// \param blueprint The blueprint to delete. @@ -88,20 +69,6 @@ IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); /// \return The project of the blueprint or null if an error occurs. IKA_API struct IkarusProject * ikarus_blueprint_get_project(IkarusBlueprint const * blueprint); -/// \brief Gets the parent folder of a blueprint. -/// \param blueprint The blueprint to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the blueprint or null if an error occurs. -IKA_API struct IkarusBlueprintFolder * ikarus_blueprint_get_parent(IkarusBlueprint const * blueprint); - -/// \brief Gets the position of a blueprint within its parent folder. -/// \param blueprint The blueprint to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the blueprint or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_position(IkarusBlueprint const * blueprint); - /// \brief Gets the name of a blueprint. /// \param blueprint The blueprint to get the name of. /// \pre \li Must not be null. @@ -110,14 +77,6 @@ IKA_API size_t ikarus_blueprint_get_position(IkarusBlueprint const * blueprint); /// \remark The returned pointer is valid until the blueprint is freed but may be invalidated by other operations. IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint); -/// \brief Gets the property root folder of a blueprint. -/// \param blueprint The blueprint to get the root folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The root folder of all properties of the blueprint or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyFolder * ikarus_blueprint_get_property_root_folder(IkarusBlueprint const * blueprint); - /// \brief Gets the number of properties of a blueprint. /// \param blueprint The blueprint to get the number of properties of. /// \pre \li Must not be null. @@ -154,29 +113,6 @@ IKA_API void ikarus_blueprint_get_linked_entities( IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size ); -/// \brief Sets the parent folder of a blueprint. -/// \param blueprint The blueprint to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the blueprint. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the blueprint in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_blueprint_set_parent( - IkarusBlueprint * blueprint, struct IkarusBlueprintFolder * new_parent, size_t new_position -); - -/// \brief Sets the position of a blueprint within its parent folder. -/// \param blueprint The blueprint to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the blueprint. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_blueprint_set_position(IkarusBlueprint * blueprint, size_t new_position); - /// \brief Sets the name of a blueprint. /// \param blueprint The blueprint to set the name of. /// \pre \li Must not be null. @@ -186,14 +122,6 @@ IKA_API void ikarus_blueprint_set_position(IkarusBlueprint * blueprint, size_t n /// \pre \li Must not be empty. IKA_API void ikarus_blueprint_set_name(IkarusBlueprint * blueprint, char const * new_name); -/// \brief Converts a blueprint to an object. -/// \param blueprint The blueprint to convert. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The constructed object, representing the blueprint. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint const * blueprint); - /// \brief Compares two blueprints. /// \param left The left blueprint to compare. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index a24cf79..e73e18d 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,6 +1,7 @@ #pragma once #include +#include /// \file entity.h /// \author Folling @@ -230,12 +231,6 @@ IKA_API void ikarus_entity_set_value( IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue * value, bool validate_settings ); -/// \brief Converts an entity to an object. -/// \param entity The entity to convert. -/// \return The constructed object, representing the entity. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity const * entity); - /// \brief Compares two entities. /// \param left The left entity to compare. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index edbbdd1..10258df 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -7,6 +7,7 @@ #include #include +#include /// \defgroup properties Properties /// \brief Properties define the structure and types of data. @@ -58,11 +59,6 @@ struct IkarusProperty; /// \param property_source The property source the property is part of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param parent_folder The parent folder of the property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the property in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the property. /// \pre \li Must not be null. /// \pre \li Must not be empty. @@ -73,8 +69,6 @@ struct IkarusProperty; IKA_API struct IkarusProperty * ikarus_property_create( struct IkarusProject * project, struct IkarusPropertySource * property_source, - struct IkarusPropertyFolder * parent_folder, - size_t position, char const * name, struct IkarusPropertyTypeInfo * property_info ); @@ -87,22 +81,13 @@ IKA_API struct IkarusProperty * ikarus_property_create( /// \param source The source to copy the property to. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param parent_folder The parent folder of the property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the property in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the property. /// \pre \li Must not be null. /// \pre \li Must not be empty. /// \return The created property or null if an error occurs. /// \remark Must be freed using #ikarus_free. IKA_API struct IkarusProperty * ikarus_property_copy( - struct IkarusProperty * property, - struct IkarusPropertySource * source, - struct IkarusPropertyFolder * parent_folder, - size_t position, - char const * name + struct IkarusProperty * property, struct IkarusPropertySource * source, char const * name ); /// \brief Deletes a property. @@ -119,20 +104,6 @@ IKA_API void ikarus_property_delete(struct IkarusProperty * property); /// \return The project of the property or null if an error occurs. IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty const * property); -/// \brief Gets the parent folder of a property. -/// \param property The property to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the property or null if an error occurs. -IKA_API struct IkarusPropertyFolder * ikarus_property_get_parent(IkarusProperty const * property); - -/// \brief Gets the position of a property within its parent folder. -/// \param property The property to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the property or undefined if an error occurs. -IKA_API size_t ikarus_property_get_position(IkarusProperty const * property); - /// \brief Gets the name of a property. /// \param property The property to get the name of. /// \pre \li Must not be null. @@ -165,29 +136,6 @@ IKA_API struct IkarusPropertySource * ikarus_property_get_source(IkarusProperty /// \remark Must be freed using #ikarus_free. IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); -/// \brief Sets the parent folder of a property. -/// \param property The property to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the property in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_property_set_parent( - IkarusProperty * property, struct IkarusPropertyFolder * new_parent, size_t new_position -); - -/// \brief Sets the position of a property within its parent folder. -/// \param property The property to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the property. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_property_set_position(IkarusProperty * property, size_t new_position); - /// \brief Sets the name of a property. /// \param property The property to set the name of. /// \pre \li Must not be null. @@ -208,11 +156,6 @@ IKA_API void ikarus_property_set_type_info( IkarusProperty * property, struct IkarusPropertyTypeInfo new_type_info, bool attempt_conversion ); -/// \brief Converts a property to an object. -/// \param property The property to convert. -/// \return The constructed object, representing the property. -IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty const * property); - /// \brief Compares two properties. /// \param left The left property to compare. /// \pre \li Must not be null. diff --git a/include/ikarus/project/project.h b/include/ikarus/persistence/project.h similarity index 100% rename from include/ikarus/project/project.h rename to include/ikarus/persistence/project.h diff --git a/include/ikarus/scopes/blueprint_scope.h b/include/ikarus/scopes/blueprint_scope.h deleted file mode 100644 index 56fa7ec..0000000 --- a/include/ikarus/scopes/blueprint_scope.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -/// \file blueprint_scope.h -/// \author Folling - -#include -#include - -/// \addtogroup object_scopes ObjectScopes -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The global scope of all blueprints. -struct IkarusBlueprintScope; - -/// \brief Creates a blueprint scope. -/// \return The created blueprint scope. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusBlueprintScope * ikarus_blueprint_scope_create(); - -/// \brief Converts a blueprint scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -/// \remark Must be freed with #ikarus_free. -IKA_API struct IkarusObjectScope * ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope); - -IKARUS_END_HEADER - -// @} diff --git a/include/ikarus/scopes/entity_scope.h b/include/ikarus/scopes/entity_scope.h deleted file mode 100644 index e1838cc..0000000 --- a/include/ikarus/scopes/entity_scope.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/// \file entity_scope.h -/// \author Folling - -#include - -/// \addtogroup object_scopes ObjectScopes -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The global scope of all entities. -struct IkarusEntityScope; - -/// \brief Creates an entity scope. -/// \return The created entity scope. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusEntityScope * ikarus_entity_scope_create(); - -/// Converts an entity scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -/// \remark Must be freed with #ikarus_free. -IKA_API struct IkarusObjectScope * ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope); - -IKARUS_END_HEADER - -// @} diff --git a/include/ikarus/scopes/object_scope.h b/include/ikarus/scopes/object_scope.h deleted file mode 100644 index 69972cf..0000000 --- a/include/ikarus/scopes/object_scope.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -// IMPLEMENTATION_DETAIL_OBJECT_SCOPES - -/// \file object_scope.h -/// \author Folling - -#include - -/// \defgroup object_scopes Object Scopes -/// \brief Scopes define where objects belong to. -/// \details They are required to differentiate between different types of objects with NULL as their parent. -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The scope of an object. -struct IkarusObjectScope; - -/// \brief The type of an object scope. -enum IkarusObjectScopeType { - /// \brief The scope is a blueprint scope. - IkarusObjectScopeType_Blueprint = 1, - /// \brief The scope is a property scope. - IkarusObjectScopeType_Property = 2, - /// \brief The scope is an entity scope. - IkarusObjectScopeType_Entity = 3 -}; - -/// \brief Visits an object scope, calling the appropriate function. -/// \param scope The scope to visit. -/// \param blueprint_visitor The function to call if the scope is an #IkarusBlueprintScope. -/// \param property_visitor The function to call if the scope is an #IkarusPropertyScope. -/// \param entity_visitor The function to call if the scope is an #IkarusEntityScope. -/// \remark function pointers may be null in which case they are not called. -IKA_API void ikarus_object_scope_visit( - IkarusObjectScope * scope, - void (*blueprint_visitor)(struct IkarusBlueprintScope *, void *), - void (*property_visitor)(struct IkarusPropertyScope *, void *), - void (*entity_visitor)(struct IkarusEntityScope *, void *), - void * data -); - -/// @} - -IKARUS_END_HEADER diff --git a/include/ikarus/scopes/property_scope.h b/include/ikarus/scopes/property_scope.h deleted file mode 100644 index d7b38db..0000000 --- a/include/ikarus/scopes/property_scope.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -/// \file property_scope.h -/// \author Folling - -#include -#include - -/// \addtogroup object_scopes ObjectScopes -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The scope of a property -struct IkarusPropertyScope; - -/// \brief Creates a property scope from a blueprint. -/// \param blueprint The blueprint the property is scoped to. -/// \return The created property scope. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusPropertyScope * ikarus_property_scope_create_blueprint(struct IkarusBlueprint * blueprint); -/// \brief Creates a property scope from an entity. -/// \param entity The entity the property is scoped to. -/// \return The created property scope. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusPropertyScope * ikarus_property_scope_create_entity(struct IkarusEntity * entity); - -/// \brief Converts a property scope to an object scope. -/// \param scope The scope to convert. -/// \return The converted scope. -/// \remark Must be freed with #ikarus_free. -IKA_API struct IkarusObjectScope * ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope); - -/// \brief Visits a property scope, calling the appropriate function. -/// \param scope The scope to to visit -/// \param blueprint_visitor The function to call if the property is scoped to a blueprint. -/// \param entity_visitor The function to call if the property is scoped to an entity. -/// \param data The data passed to the visitor functions. -IKA_API void ikarus_property_scope_visit( - IkarusPropertyScope * scope, - void (*blueprint_visitor)(struct IkarusBlueprint *, void *), - void (*entity_visitor)(struct IkarusEntity *, void *), - void * data -); - -IKARUS_END_HEADER - -// @} diff --git a/src/folders/blueprint_folder.hpp b/src/folders/blueprint_folder.hpp deleted file mode 100644 index c98b51b..0000000 --- a/src/folders/blueprint_folder.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -struct IkarusBlueprintFolder { - IkarusId id; - std::string name_buffer; -}; diff --git a/src/folders/entity_folder.hpp b/src/folders/entity_folder.hpp deleted file mode 100644 index c69e5c7..0000000 --- a/src/folders/entity_folder.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -struct IkarusEntityFolder { - IkarusId id; - std::string name_buffer; -}; diff --git a/src/folders/folder.hpp b/src/folders/folder.hpp deleted file mode 100644 index 9589a5d..0000000 --- a/src/folders/folder.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include -#include - -struct IkarusEntityFolder { - std::variant data; -}; diff --git a/src/folders/property_folder.hpp b/src/folders/property_folder.hpp deleted file mode 100644 index 11aef0c..0000000 --- a/src/folders/property_folder.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -struct IkarusPropertyFolder { - IkarusId id; - std::string name_buffer; -}; diff --git a/src/id.cpp b/src/id.cpp index b1d28fe..cc3913f 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -1,4 +1,4 @@ -#include "ikarus/id.h" +#include "id.hpp" #include @@ -15,21 +15,11 @@ auto ikarus_id_is_equal(IkarusId left, IkarusId right) -> bool { return left == right; } -auto ikarus_id_is_none(IkarusId id) -> bool { - return ikarus_id_is_equal(id, IKARUS_ID_NONE); -} - -auto ikarus_id_is_unspecified(IkarusId id) -> bool { - return ikarus_id_is_equal(id, IKARUS_ID_UNSPECIFIED); -} - TEST_CASE("id_object_type", "[id]") { // NOLINTNEXTLINE(readability-magic-numbers) auto id = static_cast(IkarusObjectType_Blueprint) << IKARUS_ID_OBJECT_RANDOM_BITS; REQUIRE(ikarus_id_get_object_type(id) == IkarusObjectType_Blueprint); - REQUIRE(!ikarus_id_is_none(id) == IkarusObjectType_Blueprint); - REQUIRE(!ikarus_id_is_unspecified(id) == IkarusObjectType_Blueprint); } TEST_CASE("id_equal", "[id]") { @@ -40,17 +30,3 @@ TEST_CASE("id_equal", "[id]") { REQUIRE(ikarus_id_is_equal(id, copy)); REQUIRE(!ikarus_id_is_equal(id, third)); } - -TEST_CASE("id_none", "[id]") { - auto id = IKARUS_ID_NONE; - - REQUIRE(ikarus_id_is_none(id)); - REQUIRE(!ikarus_id_is_unspecified(id)); -} - -TEST_CASE("id_unspecified", "[id]") { - auto id = IKARUS_ID_UNSPECIFIED; - - REQUIRE(!ikarus_id_is_none(id)); - REQUIRE(ikarus_id_is_unspecified(id)); -} diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index f938b8f..5d5e554 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include /// \private struct IkarusBlueprint { + struct IkarusProject * project; IkarusId id; }; diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index a348533..71e3cea 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include /// \private struct IkarusEntity { + struct IkarusProject * project; IkarusId id; }; diff --git a/src/objects/object.cpp b/src/objects/object.cpp deleted file mode 100644 index fe405b9..0000000 --- a/src/objects/object.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "ikarus/objects/object.h" - -#include - -#include - -IkarusObjectType ikarus_object_get_type(IkarusObject const * object) { - return object->type; -} - -TEST_CASE("object_type", "[object]") { - auto types = { - IkarusObjectType_Blueprint, - IkarusObjectType_Property, - IkarusObjectType_Entity, - IkarusObjectType_BlueprintFolder, - IkarusObjectType_PropertyFolder, - IkarusObjectType_EntityFolder, - }; - - for (auto type : types) { - auto object = IkarusObject{.type = type}; - REQUIRE(ikarus_object_get_type(&object) == type); - } -} diff --git a/src/objects/object.hpp b/src/objects/object.hpp index 0d696aa..668cd65 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -2,14 +2,12 @@ #include -#include -#include -#include #include #include #include -struct IkarusObject { - std::variant - data; +union IkarusObject { + IkarusBlueprint blueprint; + IkarusEntity entity; + IkarusProperty property; }; diff --git a/src/objects/property.hpp b/src/objects/property.hpp index f72ad8c..5132207 100644 --- a/src/objects/property.hpp +++ b/src/objects/property.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include /// \private struct IkarusProperty { + struct IkarusProject * project; IkarusId id; }; diff --git a/src/persistence/migrations/m1_initial_layout.sql b/src/persistence/migrations/m1_initial_layout.sql new file mode 100644 index 0000000..60ecc47 --- /dev/null +++ b/src/persistence/migrations/m1_initial_layout.sql @@ -0,0 +1,111 @@ +CREATE TABLE `objects` +( + `do_not_access_rowid_alias` INTEGER PRIMARY KEY, + `object_type` INT NOT NULL, + `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56) + ) VIRTUAL, + `name` TEXT NOT NULL, + `information` TEXT NOT NULL +) STRICT; + +CREATE UNIQUE INDEX `object_id` ON `objects` (`id`); +CREATE INDEX `object_type` ON `objects` (`object_type`); + +CREATE + VIRTUAL TABLE `objects_fts` USING fts5 +( + `name`, + `information`, + content= + 'objects', + content_rowid= + 'id', + tokenize= + "unicode61 remove_diacritics 2 tokenchars '-_'" +); + +CREATE TABLE `blueprints` +( + `id` INT, + + PRIMARY KEY (`id`), + FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE +) WITHOUT ROWID, STRICT; + +CREATE TABLE `entities` +( + `id` INT, + + PRIMARY KEY (`id`), + FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE +) WITHOUT ROWID, STRICT; + +CREATE TABLE `entity_blueprints` +( + `entity` INT NOT NULL, + `blueprint` INT NOT NULL, + + PRIMARY KEY (`entity`), + UNIQUE (`entity`, `blueprint`), + FOREIGN KEY (`blueprint`) REFERENCES `blueprints` (`id`) ON DELETE CASCADE +) WITHOUT ROWID, STRICT; + +CREATE INDEX `entity_blueprints_blueprint` ON `entity_blueprints` (`blueprint`); + +CREATE TABLE `properties` +( + `id` INT, + `type` INT NOT NULL, + `default_value` TEXT NOT NULL, + `settings` TEXT NOT NULL, + + PRIMARY KEY (`id`), + FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE +) WITHOUT ROWID, STRICT; + +CREATE INDEX `properties_type` ON `properties` (`type`); + +CREATE + VIRTUAL TABLE `property_default_value_fts` USING fts5 +( + `default_value`, + content= + 'properties', + content_rowid= + 'object_id', + tokenize= + "unicode61 remove_diacritics 2 tokenchars '-_'" +); + +CREATE + VIRTUAL TABLE `property_settings_fts` USING fts5 +( + `settings`, + content= + 'properties', + content_rowid= + 'object_id', + tokenize= + "unicode61 remove_diacritics 2 tokenchars '-_'" +); + +CREATE TABLE `values` +( + `entity` INT NOT NULL, + `property` INT NOT NULL, + `value` TEXT NOT NULL, + + PRIMARY KEY (`entity`, `property`), + FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`property`) REFERENCES `properties` (`id`) ON DELETE CASCADE +) WITHOUT ROWID, STRICT; + +CREATE + VIRTUAL TABLE `values_fts` USING fts5 +( + `value`, + content= + 'values', + tokenize= + "unicode61 remove_diacritics 2 tokenchars '-_'" +) diff --git a/src/projects/project.hpp b/src/persistence/project.hpp similarity index 61% rename from src/projects/project.hpp rename to src/persistence/project.hpp index c805935..db52508 100644 --- a/src/projects/project.hpp +++ b/src/persistence/project.hpp @@ -1,10 +1,11 @@ -#include "ikarus/project/project.h" - #include #include +#include + /// \private struct IkarusProject { std::string name; std::filesystem::path path; + std::unique_ptr db; }; diff --git a/src/scopes/blueprint_scope.cpp b/src/scopes/blueprint_scope.cpp deleted file mode 100644 index c1eb0fc..0000000 --- a/src/scopes/blueprint_scope.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "ikarus/scopes/blueprint_scope.h" - -#include -#include - -IkarusBlueprintScope * ikarus_blueprint_scope_create() { - return new IkarusBlueprintScope{}; -} - -struct IkarusObjectScope * ikarus_blueprint_scope_to_object_scope(IkarusBlueprintScope const * scope) { - return new IkarusObjectScope{.data = *scope}; -} diff --git a/src/scopes/blueprint_scope.hpp b/src/scopes/blueprint_scope.hpp deleted file mode 100644 index ba0bc7d..0000000 --- a/src/scopes/blueprint_scope.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -struct IkarusBlueprintScope {}; diff --git a/src/scopes/entity_scope.cpp b/src/scopes/entity_scope.cpp deleted file mode 100644 index a0f78d0..0000000 --- a/src/scopes/entity_scope.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "ikarus/scopes/entity_scope.h" - -#include -#include - -IkarusEntityScope * ikarus_entity_scope_create() { - return new IkarusEntityScope{}; -} - -IkarusObjectScope * ikarus_entity_scope_to_object_scope(IkarusEntityScope const * scope) { - return new IkarusObjectScope{.data = *scope}; -} diff --git a/src/scopes/entity_scope.hpp b/src/scopes/entity_scope.hpp deleted file mode 100644 index 6df6550..0000000 --- a/src/scopes/entity_scope.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -struct IkarusEntityScope {}; diff --git a/src/scopes/object_scope.cpp b/src/scopes/object_scope.cpp deleted file mode 100644 index ddae9a9..0000000 --- a/src/scopes/object_scope.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "ikarus/scopes/object_scope.h" - -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include - -IkarusObjectScopeType ikarus_object_scope_get_type(IkarusObjectScope * scope) { - return std::visit( - cppbase::overloaded{ - []([[maybe_unused]] IkarusBlueprintScope const& scope) { return IkarusObjectScopeType_Blueprint; }, - []([[maybe_unused]] IkarusPropertyScope const& scope) { return IkarusObjectScopeType_Property; }, - []([[maybe_unused]] IkarusEntityScope const& scope) { return IkarusObjectScopeType_Entity; }}, - scope->data - ); -} - -void ikarus_object_scope_visit( - IkarusObjectScope * scope, - void(blueprint_visitor)(IkarusBlueprintScope *, void *), - void(property_visitor)(IkarusPropertyScope *, void *), - void(entity_visitor)(IkarusEntityScope *, void *), - void * data -) { - std::visit( - cppbase::overloaded{ - [blueprint_visitor, data](IkarusBlueprintScope& scope) { blueprint_visitor(&scope, data); }, - [property_visitor, data](IkarusPropertyScope& scope) { property_visitor(&scope, data); }, - [entity_visitor, data](IkarusEntityScope& scope) { entity_visitor(&scope, data); }}, - scope->data - ); -} - -TEST_CASE("blueprint_object_scope_conversion", "[object_scope]") { - auto * blueprint_scope = ikarus_blueprint_scope_create(); - auto * blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); - REQUIRE(ikarus_object_scope_get_type(blueprint_object_scope) == IkarusObjectScopeType_Blueprint); -} - -TEST_CASE("property_object_scope_conversion", "[object_scope]") { - auto * blueprint = ikarus_blueprint_create(); - auto * entity = ikarus_entity_create(); - - auto * property_blueprint_scope = ikarus_property_scope_create_blueprint(blueprint); - auto * property_blueprint_object_scope = ikarus_property_scope_to_object_scope(property_blueprint_scope); - - REQUIRE(ikarus_object_scope_get_type(property_blueprint_object_scope) == IkarusObjectScopeType_Property); - - auto * property_entity_scope = ikarus_property_scope_create_entity(entity); - auto * property_entity_object_scope = ikarus_property_scope_to_object_scope(property_entity_scope); - - REQUIRE(ikarus_object_scope_get_type(property_entity_object_scope) == IkarusObjectScopeType_Property); -} - -TEST_CASE("entity_object_scope_conversion", "[object_scope]") { - auto * entity_scope = ikarus_entity_scope_create(); - auto * entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); - REQUIRE(ikarus_object_scope_get_type(entity_object_scope) == IkarusObjectScopeType_Entity); -} - -TEST_CASE("object_scope_visiting", "[object_scope]") { - auto * blueprint = ikarus_blueprint_create(); - - auto * blueprint_scope = ikarus_blueprint_scope_create(); - auto * property_scope = ikarus_property_scope_create_blueprint(blueprint); - auto * entity_scope = ikarus_entity_scope_create(); - - auto * blueprint_object_scope = ikarus_blueprint_scope_to_object_scope(blueprint_scope); - auto * property_object_scope = ikarus_property_scope_to_object_scope(property_scope); - auto * entity_object_scope = ikarus_entity_scope_to_object_scope(entity_scope); - - auto scopes = { - std::make_pair(blueprint_object_scope, 1), - std::make_pair(property_object_scope, 2), - std::make_pair(entity_object_scope, 3), - }; - - for (auto [scope, value] : scopes) { - int test = 0; - - ikarus_object_scope_visit( - scope, - [](IkarusBlueprintScope *, void * data) { *static_cast(data) = 1; }, - [](IkarusPropertyScope *, void * data) { *static_cast(data) = 2; }, - [](IkarusEntityScope *, void * data) { *static_cast(data) = 3; }, - &test - ); - - REQUIRE(test == value); - } -} diff --git a/src/scopes/object_scope.hpp b/src/scopes/object_scope.hpp deleted file mode 100644 index 9bc3b09..0000000 --- a/src/scopes/object_scope.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -struct IkarusObjectScope { - std::variant data; -}; diff --git a/src/scopes/property_scope.cpp b/src/scopes/property_scope.cpp deleted file mode 100644 index 26f45f5..0000000 --- a/src/scopes/property_scope.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "ikarus/scopes/property_scope.h" - -#include - -#include -#include -#include -#include - -IkarusPropertyScope * ikarus_property_scope_create_blueprint(IkarusBlueprint * blueprint) { - return new IkarusPropertyScope{.data = *blueprint}; -} - -IkarusPropertyScope * ikarus_property_scope_create_entity(IkarusEntity * entity) { - return new IkarusPropertyScope{.data = *entity}; -} - -IkarusObjectScope * ikarus_property_scope_to_object_scope(IkarusPropertyScope const * scope) { - return new IkarusObjectScope{.data = *scope}; -} - -void ikarus_property_scope_visit( - IkarusPropertyScope * scope, - void (*blueprint_func)(IkarusBlueprint *, void *), - void (*entity_func)(IkarusEntity *, void *), - void * data -) { - std::visit( - cppbase::overloaded{ - [blueprint_func, data](IkarusBlueprint& blueprint) { blueprint_func(&blueprint, data); }, - [entity_func, data](IkarusEntity& entity) { entity_func(&entity, data); }, - }, - scope->data - ); -} diff --git a/src/scopes/property_scope.hpp b/src/scopes/property_scope.hpp deleted file mode 100644 index 4d3aad0..0000000 --- a/src/scopes/property_scope.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include - -struct IkarusPropertyScope { - std::variant data; -}; diff --git a/src/values/value.cpp b/src/values/value.cpp deleted file mode 100644 index a5d6457..0000000 --- a/src/values/value.cpp +++ /dev/null @@ -1,343 +0,0 @@ -#include "ikarus/values/value.h" - -#include -#include -#include -#include -#include - -#include - -#include -#include - -/// \brief Creates an indeterminate entity value of a given type. -/// \param type The type of the value. -/// \return The entity value. -IKA_API IkarusEntityValue value_create_invalid(IkarusPropertyType type) { - return IkarusEntityValue{ - ._type = type, - ._data = IkarusEntityValueData{}, - ._state = IkarusEntityValueState_Invalid, - }; -} - -IkarusEntityValue ikarus_value_create_toggle(bool value) { - return IkarusEntityValue{ - ._type = IkarusPropertyType_Toggle, - ._data = IkarusEntityValueData{._toggle = IkarusToggleValue{._value = value}}, - ._state = IkarusEntityValueState_Normal, - }; -} - -IkarusEntityValue ikarus_value_create_number(long double value) { - if (auto fp_class = std::fpclassify(value); fp_class != FP_NORMAL && fp_class != FP_ZERO) { - return value_create_invalid(IkarusPropertyType_Number); - } - - return IkarusEntityValue{ - ._type = IkarusPropertyType_Number, - ._data = IkarusEntityValueData{._number = IkarusNumberValue{._value = value}}, - ._state = IkarusEntityValueState_Normal, - }; -} - -IkarusEntityValue ikarus_value_create_text(char const * value) { - if (value == nullptr) { - return value_create_invalid(IkarusPropertyType_Text); - } - - return IkarusEntityValue{ - ._type = IkarusPropertyType_Text, - ._data = IkarusEntityValueData{._text = IkarusTextValue{._value = value}}, - ._state = IkarusEntityValueState_Normal, - }; -} - -IkarusEntityValue ikarus_value_create_indeterminate(IkarusPropertyType type) { - IkarusEntityValueData data{}; - - switch (type) { - case IkarusPropertyType_Toggle: { - data._toggle = IkarusToggleValue{._value = false}; - break; - } - case IkarusPropertyType_Number: { - data._number = IkarusNumberValue{._value = 0.0}; - break; - } - case IkarusPropertyType_Text: { - data._text = IkarusTextValue{._value = ""}; - break; - } - default: return value_create_invalid(type); - } - - return IkarusEntityValue{ - ._type = type, - ._data = data, - ._state = IkarusEntityValueState_Indeterminate, - }; -} - -IkarusEntityValue ikarus_value_get_default(IkarusPropertyType type) { - switch (type) { - case IkarusPropertyType_Toggle: return ikarus_value_create_toggle(false); - case IkarusPropertyType_Number: return ikarus_value_create_number(0.0); - case IkarusPropertyType_Text: return ikarus_value_create_text(""); - default: return value_create_invalid(type); - } -} - -bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value) { - return value->_value; -} - -long double ikarus_number_value_get_underlying(IkarusNumberValue const * value) { - return value->_value; -} - -char const * ikarus_text_value_get_underlying(IkarusTextValue const * value) { - return value->_value; -} - -// no need to check for validity here, since these concrete types are only created by the library -bool ikarus_toggle_value_is_equal(IkarusToggleValue const * value, bool check) { - return value != nullptr && value->_value == check; -} - -bool ikarus_number_value_is_equal(IkarusNumberValue const * value, long double check) { - return value != nullptr && value->_value == check; -} - -bool ikarus_text_value_is_equal(IkarusTextValue const * value, char const * check) { - return value != nullptr && check != nullptr && std::strcmp(value->_value, check) == 0; -} - -bool ikarus_value_is_equal(IkarusEntityValue const * left, IkarusEntityValue const * right) { - if (left == nullptr || right == nullptr) { - return false; - } - - if (left->_state == IkarusEntityValueState_Invalid || right->_state == IkarusEntityValueState_Invalid) { - return false; - } - - if (left->_type != right->_type) { - return false; - } - - // indeterminate values are only equal if they have the same type - if (left->_state == IkarusEntityValueState_Indeterminate && right->_state == IkarusEntityValueState_Indeterminate) { - return true; - } - - switch (left->_type) { - case IkarusPropertyType_Toggle: return left->_data._toggle._value == right->_data._toggle._value; - case IkarusPropertyType_Number: return left->_data._number._value == right->_data._number._value; - case IkarusPropertyType_Text: return std::strcmp(left->_data._text._value, right->_data._text._value) == 0; - default: return false; - } -} - -bool ikarus_value_is_invalid(IkarusEntityValue const * value) { - return value == nullptr || value->_state == IkarusEntityValueState_Invalid; -} - -IkarusPropertyType ikarus_value_get_type(IkarusEntityValue const * value) { - return value->_type; -} - -void ikarus_value_visit( - IkarusEntityValue const * value, - void (*toggle)(IkarusToggleValue const * value, void * data), - void (*number)(IkarusNumberValue const * value, void * data), - void (*text)(IkarusTextValue const * value, void * data), - void * data -) { - if (value == nullptr) { - return; - } - - switch (value->_type) { - case IkarusPropertyType_Toggle: { - if (toggle != nullptr) { - toggle(&value->_data._toggle, data); - } - break; - } - case IkarusPropertyType_Number: { - if (number != nullptr) { - number(&value->_data._number, data); - } - break; - } - case IkarusPropertyType_Text: { - if (text != nullptr) { - text(&value->_data._text, data); - } - break; - } - default: break; - } -} - -TEST_CASE("toggle_value_creation", "[value]") { - auto toggle_value = ikarus_value_create_toggle(true); - - REQUIRE(ikarus_value_get_type(&toggle_value) == IkarusPropertyType_Toggle); - REQUIRE(ikarus_toggle_value_is_equal(&toggle_value._data._toggle, true)); -} - -TEST_CASE("number_value_creation", "[value]") { - auto number_value = ikarus_value_create_number(1.0); - - REQUIRE(ikarus_value_get_type(&number_value) == IkarusPropertyType_Number); - REQUIRE(ikarus_number_value_is_equal(&number_value._data._number, 1.0)); - - auto nan_value = ikarus_value_create_number(std::numeric_limits::quiet_NaN()); - REQUIRE(ikarus_value_is_invalid(&nan_value)); - auto signaling_non_value = ikarus_value_create_number(std::numeric_limits::signaling_NaN()); - REQUIRE(ikarus_value_is_invalid(&signaling_non_value)); - auto inf_value = ikarus_value_create_number(std::numeric_limits::infinity()); - REQUIRE(ikarus_value_is_invalid(&inf_value)); - auto neg_inf_value = ikarus_value_create_number(-std::numeric_limits::infinity()); - REQUIRE(ikarus_value_is_invalid(&neg_inf_value)); -} - -TEST_CASE("text_value_creation", "[value]") { - auto text_value = ikarus_value_create_text("test"); - - REQUIRE(ikarus_value_get_type(&text_value) == IkarusPropertyType_Text); - REQUIRE(ikarus_text_value_is_equal(&text_value._data._text, "test")); - - auto null_value = ikarus_value_create_text(nullptr); - REQUIRE(ikarus_value_is_invalid(&null_value)); -} - -TEST_CASE("default_value_creation", "[value]") { - auto types = { - IkarusPropertyType_Toggle, - IkarusPropertyType_Number, - IkarusPropertyType_Text, - }; - - for (auto type : types) { - auto value = ikarus_value_get_default(type); - REQUIRE(ikarus_value_get_type(&value) == type); - } -} - -TEST_CASE("toggle_value_underlying", "[value]") { - auto true_toggle_value = ikarus_value_create_toggle(true); - auto false_toggle_value = ikarus_value_create_toggle(false); - - REQUIRE(ikarus_toggle_value_get_underlying(&true_toggle_value._data._toggle) == true); - REQUIRE(ikarus_toggle_value_get_underlying(&false_toggle_value._data._toggle) == false); -} - -TEST_CASE("number_value_underlying", "[value]") { - auto zero_number_value = ikarus_value_create_number(0.0); - auto third_number_value = ikarus_value_create_number(1.0 / 3.0); - auto large_number_value = ikarus_value_create_number(1.2345678910e123); - - REQUIRE(ikarus_number_value_get_underlying(&zero_number_value._data._number) == 0.0); - REQUIRE(ikarus_number_value_get_underlying(&third_number_value._data._number) == 1.0 / 3.0); - REQUIRE(ikarus_number_value_get_underlying(&large_number_value._data._number) == 1.2345678910e123); -} - -TEST_CASE("text_value_underlying", "[value]") { - auto test_value = ikarus_value_create_text("test"); - auto empty_value = ikarus_value_create_text(""); - - REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&test_value._data._text), "test") == 0); - REQUIRE(std::strcmp(ikarus_text_value_get_underlying(&empty_value._data._text), "") == 0); -} - -TEST_CASE("toggle_comparison", "[value]") { - auto true_toggle_value = ikarus_value_create_toggle(true); - auto false_toggle_value = ikarus_value_create_toggle(false); - - REQUIRE(ikarus_toggle_value_is_equal(&true_toggle_value._data._toggle, true)); - REQUIRE(ikarus_toggle_value_is_equal(&false_toggle_value._data._toggle, false)); -} - -TEST_CASE("number_comparison", "[value]") { - auto zero_number_value = ikarus_value_create_number(0.0); - auto third_number_value = ikarus_value_create_number(1.0 / 3.0); - auto large_number_value = ikarus_value_create_number(1.2345678910e123); - - REQUIRE(ikarus_number_value_is_equal(&zero_number_value._data._number, 0.0)); - REQUIRE(ikarus_number_value_is_equal(&third_number_value._data._number, 1.0 / 6.0 + 1.0 / 6.0)); - REQUIRE(ikarus_number_value_is_equal(&large_number_value._data._number, 1.2345678910e123)); -} - -TEST_CASE("text_comparison", "[value]") { - auto test_value = ikarus_value_create_text("test"); - auto empty_value = ikarus_value_create_text(""); - - REQUIRE(ikarus_text_value_is_equal(&test_value._data._text, "test")); - REQUIRE(ikarus_text_value_is_equal(&empty_value._data._text, "")); -} - -TEST_CASE("value_comparison", "[value]") { - auto true_toggle_value = ikarus_value_create_toggle(true); - auto false_toggle_value = ikarus_value_create_toggle(false); - auto number_value1 = ikarus_value_create_number(0.0); - auto number_value2 = ikarus_value_create_number(0.0); - auto invalid_value = ikarus_value_create_text(nullptr); - - auto indeterminate_toggle = ikarus_value_create_indeterminate(IkarusPropertyType_Toggle); - auto indeterminate_number1 = ikarus_value_create_indeterminate(IkarusPropertyType_Number); - auto indeterminate_number2 = ikarus_value_create_indeterminate(IkarusPropertyType_Number); - - REQUIRE(!ikarus_value_is_equal(nullptr, nullptr)); - REQUIRE(!ikarus_value_is_equal(&true_toggle_value, nullptr)); - REQUIRE(!ikarus_value_is_equal(nullptr, &true_toggle_value)); - - REQUIRE(!ikarus_value_is_equal(&invalid_value, &invalid_value)); - REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &invalid_value)); - REQUIRE(!ikarus_value_is_equal(&invalid_value, &true_toggle_value)); - - REQUIRE(ikarus_value_is_equal(&true_toggle_value, &true_toggle_value)); - REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &false_toggle_value)); - REQUIRE(!ikarus_value_is_equal(&true_toggle_value, &number_value1)); - REQUIRE(!ikarus_value_is_equal(&number_value1, &true_toggle_value)); - REQUIRE(ikarus_value_is_equal(&number_value1, &number_value2)); - - REQUIRE(!ikarus_value_is_equal(&indeterminate_toggle, &indeterminate_number1)); - REQUIRE(ikarus_value_is_equal(&indeterminate_number1, &indeterminate_number2)); -} - -TEST_CASE("invalid_value", "[value]") { - auto invalid_value = ikarus_value_create_toggle(false); - invalid_value._state = IkarusEntityValueState_Invalid; - - REQUIRE(ikarus_value_is_invalid(&invalid_value)); -} - -TEST_CASE("visit_value", "[value]") { - auto toggle_value = ikarus_value_create_toggle(true); - auto number_value = ikarus_value_create_number(0.0); - auto text_value = ikarus_value_create_text("test"); - - auto values = { - std::make_pair(toggle_value, 1), - std::make_pair(number_value, 2), - std::make_pair(text_value, 3), - }; - - for (auto [value, expected] : values) { - int test = 0; - - ikarus_value_visit( - &value, - [](IkarusToggleValue const *, void * data) { *reinterpret_cast(data) = 1; }, - [](IkarusNumberValue const *, void * data) { *reinterpret_cast(data) = 2; }, - [](IkarusTextValue const *, void * data) { *reinterpret_cast(data) = 3; }, - &test - ); - - REQUIRE(test == expected); - } -} From 539d29521a339d4badea32cc1386ca47b0e1c641 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 14 Nov 2023 16:29:41 +0100 Subject: [PATCH 092/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index a397a5e..3057656 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit a397a5e8c35c7b30ecac8b4994301a585e24560b +Subproject commit 3057656ff277294ab424af90e553e630c2a5e8f7 From 649af864c40fd055d43b06f048451f6a7627a61a Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 18 Nov 2023 02:10:20 +0100 Subject: [PATCH 093/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 3057656..806c264 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 3057656ff277294ab424af90e553e630c2a5e8f7 +Subproject commit 806c26457c4e9e3d613e63a8511150390c1b196d From 632b4853b95f79fe87b694ed817670ae4c10f128 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 18 Nov 2023 02:10:38 +0100 Subject: [PATCH 094/166] implement blueprint functionality & streamline API Signed-off-by: Folling --- include/ikarus/objects/blueprint.h | 67 +-------- include/ikarus/objects/entity.h | 100 +------------ include/ikarus/objects/property.h | 55 +------ src/objects/blueprint.cpp | 135 ++++++++++++++++++ src/objects/blueprint.hpp | 8 +- src/objects/entity.hpp | 7 +- src/objects/object.hpp | 15 +- src/objects/property.hpp | 8 +- .../migrations/m1_initial_layout.sql | 9 +- 9 files changed, 175 insertions(+), 229 deletions(-) create mode 100644 src/objects/blueprint.cpp diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index ed35c89..85f40eb 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -28,55 +28,13 @@ struct IkarusBlueprint; /// \remark Must be freed using #ikarus_free. IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name); -/// \brief Creates a blueprint from an entity. -/// \details The created blueprint will have the same properties as the entity. -/// \param entity The entity to create the blueprint from. -/// \pre \li Must not be null. -/// \param link_entity If true, the entity will be linked to the blueprint. If not they will remain separate. -/// \param name The name of the blueprint. Must not be empty. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created blueprint or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprint * ikarus_blueprint_create_from_entity( - struct IkarusEntity * entity, bool link_entity, char const * name -); - -/// \brief Copies a blueprint. -/// \details Creates a deep copy of the blueprint including all of its properties. -/// \param blueprint The blueprint to copy. -/// \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. -/// \return The created blueprint or null if an error occurs. -/// \remark Linked entities won't be copied. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprint * ikarus_blueprint_copy(IkarusBlueprint const * blueprint, char const * name); - -/// \brief Deletes a blueprint. +/// \brief Deletes & frees a blueprint. /// \param blueprint The blueprint to delete. /// \pre \li Must not be null. /// \pre \li Must exist. /// \remark The blueprint must not be accessed after deletion. IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); -/// \brief Gets the project of a blueprint. -/// \param blueprint The blueprint to get the project of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The project of the blueprint or null if an error occurs. -IKA_API struct IkarusProject * ikarus_blueprint_get_project(IkarusBlueprint const * blueprint); - -/// \brief Gets the name of a blueprint. -/// \param blueprint The blueprint to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the blueprint or null if an error occurs. -/// \remark The returned pointer is valid until the blueprint is freed but may be invalidated by other operations. -IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint); - /// \brief Gets the number of properties of a blueprint. /// \param blueprint The blueprint to get the number of properties of. /// \pre \li Must not be null. @@ -113,26 +71,13 @@ IKA_API void ikarus_blueprint_get_linked_entities( IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size ); -/// \brief Sets the name of a blueprint. -/// \param blueprint The blueprint to set the name of. +/// \brief Casts a blueprint to an object. +/// \param blueprint The blueprint to cast. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param new_name The new name of the blueprint. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_blueprint_set_name(IkarusBlueprint * blueprint, char const * new_name); - -/// \brief Compares two blueprints. -/// \param left The left blueprint to compare. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param right The right blueprint to compare. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return True if the two blueprints are equal, false otherwise. -/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two -/// blueprints reference the same blueprint in the same project. -IKA_API bool ikarus_blueprint_is_equal(IkarusBlueprint const * left, IkarusBlueprint const * right); +/// \return The blueprint represented as an object or null if an error occurs. +/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. +IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint const * blueprint); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index e73e18d..813adea 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -58,25 +58,6 @@ IKA_API IkarusEntity * ikarus_entity_create( size_t blueprints_count ); -/// \brief Copies an entity. -/// \details Creates a deep copy of the entity including all of its properties & associated values. -/// \param entity The entity to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param parent The parent folder of the entity. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the entity in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \param name The name of the entity. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \return The created entity or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API IkarusEntity * ikarus_entity_copy( - struct IkarusEntity * entity, struct IkarusEntityFolder * parent, size_t position, char const * name -); - /// \brief Deletes an entity. /// \param entity The entity to delete. /// \pre \li Must not be null. @@ -117,43 +98,6 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru /// \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); -/// \brief Gets the project of an entity. -/// \param entity The entity to get the project of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The project of the entity or null if an error occurs. -IKA_API struct IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity); - -/// \brief Gets the parent folder of an entity. -/// \param entity The entity to get the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The parent folder of the entity or null if an error occurs. -IKA_API struct IkarusEntityFolder * ikarus_entity_get_parent(IkarusEntity const * entity); - -/// \brief Gets the position of an entity within its parent folder. -/// \param entity The entity to get the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The position of the entity or undefined if an error occurs. -IKA_API size_t ikarus_entity_get_position(IkarusEntity const * entity); - -/// \brief Gets the name of an entity. -/// \param entity The entity to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the entity or null if an error occurs. -/// \remark The returned pointer is valid until the entity is freed but may be invalidated by other operations. -IKA_API char const * ikarus_entity_get_name(IkarusEntity const * entity); - -/// \brief Gets the property root folder of an entity. -/// \param entity The entity to get the root folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The root folder of all properties of the entity or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyFolder * ikarus_entity_get_property_root_folder(IkarusEntity const * entity); - /// \brief Gets the number of properties of an entity. /// \param entity The entity to get the number of properties of. /// \pre \li Must not be null. @@ -184,36 +128,6 @@ IKA_API void ikarus_entity_get_properties( /// \remark Must be freed using #ikarus_free. IKA_API struct IkarusEntityValue * get_value(IkarusEntity const * entity, struct IkarusProperty const * property); -/// \brief Sets the parent folder of an entity. -/// \param entity The entity to set the parent folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_parent The new parent folder of the entity. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the entity in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of old and new siblings. -IKA_API void ikarus_entity_set_parent(IkarusEntity * entity, struct IkarusEntityFolder * new_parent, size_t new_position); - -/// \brief Sets the position of an entity within its parent folder. -/// \param entity The entity to set the position of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_position The new position of the entity. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. -/// \remark This adjusts the positions of siblings. -IKA_API void ikarus_entity_set_position(IkarusEntity * entity, size_t new_position); - -/// \brief Sets the name of an entity. -/// \param entity The entity to set the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_name The new name of the entity. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_entity_set_name(IkarusEntity * entity, char const * new_name); - /// \brief Sets the value of a property of an entity. /// \param entity The entity to set the value of. /// \pre \li Must not be null. @@ -231,17 +145,13 @@ IKA_API void ikarus_entity_set_value( IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue * value, bool validate_settings ); -/// \brief Compares two entities. -/// \param left The left entity to compare. +/// \brief Casts an entity to an object. +/// \param entity The entity to cast. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param right The right entity to compare. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return True if the two entities are equal, false otherwise. -/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two -/// entities reference the same entity in the same project. -IKA_API bool ikarus_entity_is_equal(IkarusEntity const * left, IkarusEntity const * right); +/// \return The entity represented as an object or null if an error occurs. +/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. +IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity const * entity); IKARUS_END_HEADER diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index 10258df..a62a821 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -73,23 +73,6 @@ IKA_API struct IkarusProperty * ikarus_property_create( struct IkarusPropertyTypeInfo * property_info ); -/// \brief Copies a property. -/// \details Creates a deep copy of the property including all of its settings and associated values. -/// \param property The property to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param source The source to copy the property to. -/// \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. -/// \return The created property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusProperty * ikarus_property_copy( - struct IkarusProperty * property, struct IkarusPropertySource * source, char const * name -); - /// \brief Deletes a property. /// \param property The property to delete. /// \pre \li Must not be null. @@ -97,21 +80,6 @@ IKA_API struct IkarusProperty * ikarus_property_copy( /// \remark The property must not be accessed after deletion. IKA_API void ikarus_property_delete(struct IkarusProperty * property); -/// \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. -/// \return The project of the property or null if an error occurs. -IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty const * property); - -/// \brief Gets the name of a property. -/// \param property The property to get the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The name of the property or null if an error occurs. -/// \remark The returned pointer is valid until the property is freed but may be invalidated by other operations. -IKA_API char const * ikarus_property_get_name(IkarusProperty const * property); - /// \brief Gets the type info of a property. /// \param property The property to get the type info of. /// \pre \li Must not be null. @@ -136,15 +104,6 @@ IKA_API struct IkarusPropertySource * ikarus_property_get_source(IkarusProperty /// \remark Must be freed using #ikarus_free. IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); -/// \brief Sets 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 new_name The new name of the property. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -IKA_API void ikarus_property_set_name(IkarusProperty * property, char const * new_name); - /// \brief Sets the type info of a property and resets all values to the new default value. /// \param property The property to set the type info of. /// \pre \li Must not be null. @@ -156,17 +115,13 @@ IKA_API void ikarus_property_set_type_info( IkarusProperty * property, struct IkarusPropertyTypeInfo new_type_info, bool attempt_conversion ); -/// \brief Compares two properties. -/// \param left The left property to compare. +/// \brief Casts a property to an object. +/// \param property The property to cast. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param right The right property to compare. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return True if the two properties are equal, false otherwise. -/// \remark This neither performs a pointer comparison nor a deep comparison. When we say "equal" we mean that the two -/// properties reference the same property in the same project. -IKA_API bool ikarus_property_is_equal(IkarusProperty const * left, IkarusProperty const * right); +/// \return The property represented as an object or null if an error occurs. +/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. +IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty const * property); IKARUS_END_HEADER diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp new file mode 100644 index 0000000..3d1c997 --- /dev/null +++ b/src/objects/blueprint.cpp @@ -0,0 +1,135 @@ +#include "blueprint.hpp" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { + LOG_INFO("creating new blueprint"); + + if (project == nullptr) { + LOG_ERROR("project is nullptr"); + return nullptr; + } + + LOG_VERBOSE("project={}; name={}", project->path.c_str(), name); + + if (name == nullptr) { + LOG_ERROR("name is nullptr"); + return nullptr; + } + + if (cppbase::is_empty_or_blank(name)) { + LOG_ERROR("name is empty or blank"); + return nullptr; + } + + VTRYRV(auto id, nullptr, project->db->transact([name](auto * db) -> cppbase::Result { + LOG_VERBOSE("creating blueprint in objects table"); + + TRY(db->execute( + "INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(IkarusObjectType_Blueprint), name + )); + + auto id = db->last_insert_rowid(); + + LOG_VERBOSE("blueprint is {}", id); + + LOG_VERBOSE("inserting blueprint into blueprints table"); + + TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?);", id)); + + return cppbase::ok(id); + })); + + LOG_VERBOSE("successfully created blueprint"); + + return new IkarusBlueprint{project, id}; +} + +void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { + LOG_INFO("deleting blueprint"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return; + } + + LOG_VERBOSE("blueprint={}", blueprint->id); + + if (auto res = blueprint->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id); res.is_err()) { + LOG_ERROR("failed to delete blueprint {} from objects table: {}", blueprint->id, res.unwrap_error()); + return; + } + + LOG_VERBOSE("blueprint was successfully deleted from database, freeing pointer"); + + delete blueprint; + + LOG_VERBOSE("successfully deleted blueprint"); +} + +size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { + LOG_VERBOSE("fetching blueprint property count"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return 0; + } + + LOG_VERBOSE("blueprint={}", blueprint->id); + + VTRYRV( + auto count, + 0, + blueprint->project->db->query_one( + "SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint_id` = ?;", blueprint->id + ) + ); + + return static_cast(count); +} + +void ikarus_blueprint_get_properties( + IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size +) { + LOG_VERBOSE("fetching blueprint properties"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return; + } + + if (properties_out == nullptr) { + LOG_ERROR("properties_out is nullptr"); + return; + } + + LOG_VERBOSE("blueprint={}; properties_out_size={}", blueprint->id, properties_out_size); + + IkarusId ids[properties_out_size]; + + if (auto res = blueprint->project->db->query_many_buffered( + "SELECT `id` FROM `properties` WHERE `source` = ?", static_cast(ids), properties_out_size, blueprint->id + ); + res.is_err()) { + LOG_ERROR("failed to fetch blueprint property ids: {}", res.unwrap_error()); + return; + } + + for (size_t i = 0; i < properties_out_size; ++i) { + /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + properties_out[i] = new IkarusProperty{blueprint->project, ids[i]}; + } + + LOG_VERBOSE("successfully fetched blueprint properties"); +} diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index 5d5e554..f904074 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -1,9 +1,9 @@ #pragma once -#include +#include /// \private -struct IkarusBlueprint { - struct IkarusProject * project; - IkarusId id; +struct IkarusBlueprint : public IkarusObject { + inline IkarusBlueprint(struct IkarusProject * project, IkarusId id): + IkarusObject{project, id} {} }; diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index 71e3cea..91b3d3e 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -1,9 +1,6 @@ #pragma once -#include +#include /// \private -struct IkarusEntity { - struct IkarusProject * project; - IkarusId id; -}; +struct IkarusEntity : public IkarusObject {}; diff --git a/src/objects/object.hpp b/src/objects/object.hpp index 668cd65..e95c3e0 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -2,12 +2,13 @@ #include -#include -#include -#include +#include -union IkarusObject { - IkarusBlueprint blueprint; - IkarusEntity entity; - IkarusProperty property; +struct IkarusObject { + struct IkarusProject * project; + IkarusId id; + + inline IkarusObject(struct IkarusProject * project, IkarusId id): + project{project}, + id{id} {} }; diff --git a/src/objects/property.hpp b/src/objects/property.hpp index 5132207..b59d7dd 100644 --- a/src/objects/property.hpp +++ b/src/objects/property.hpp @@ -1,9 +1,9 @@ #pragma once -#include +#include /// \private -struct IkarusProperty { - struct IkarusProject * project; - IkarusId id; +struct IkarusProperty : public IkarusObject { + inline IkarusProperty(struct IkarusProject * project, IkarusId id): + IkarusObject{project, id} {} }; diff --git a/src/persistence/migrations/m1_initial_layout.sql b/src/persistence/migrations/m1_initial_layout.sql index 60ecc47..7b49cb6 100644 --- a/src/persistence/migrations/m1_initial_layout.sql +++ b/src/persistence/migrations/m1_initial_layout.sql @@ -2,8 +2,7 @@ CREATE TABLE `objects` ( `do_not_access_rowid_alias` INTEGER PRIMARY KEY, `object_type` INT NOT NULL, - `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56) - ) VIRTUAL, + `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56)) VIRTUAL, `name` TEXT NOT NULL, `information` TEXT NOT NULL ) STRICT; @@ -47,6 +46,7 @@ CREATE TABLE `entity_blueprints` PRIMARY KEY (`entity`), UNIQUE (`entity`, `blueprint`), + FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, FOREIGN KEY (`blueprint`) REFERENCES `blueprints` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; @@ -58,12 +58,15 @@ CREATE TABLE `properties` `type` INT NOT NULL, `default_value` TEXT NOT NULL, `settings` TEXT NOT NULL, + `source` INT NOT NULL, PRIMARY KEY (`id`), - FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE + FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`source`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; CREATE INDEX `properties_type` ON `properties` (`type`); +CREATE INDEX `properties_source` ON `properties` (`source`); CREATE VIRTUAL TABLE `property_default_value_fts` USING fts5 From ca03885c060b8b7cae48a2a2af68d10e3205a797 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 20 Nov 2023 08:58:58 +0100 Subject: [PATCH 095/166] cache entities to avoid allocations Signed-off-by: Folling --- include/ikarus/errors.h | 71 ++++++++++++++++++ include/ikarus/global.h | 6 +- src/errors.cpp | 22 ++++++ src/objects/blueprint.cpp | 114 ++++++++++++++++++++-------- src/objects/entity.hpp | 5 +- src/persistence/project.hpp | 143 +++++++++++++++++++++++++++++++++++- 6 files changed, 324 insertions(+), 37 deletions(-) create mode 100644 include/ikarus/errors.h create mode 100644 src/errors.cpp diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h new file mode 100644 index 0000000..f501c22 --- /dev/null +++ b/include/ikarus/errors.h @@ -0,0 +1,71 @@ +#pragma once + +/// \file global.h +/// \author Folling + +#include + +/// \addtogroup errors Errors +/// \brief Error handling within libikarus +/// \details Errors are stored for each project, akin to the errno handling in C. +/// We store multiple pieces of information about the error occurring. For more information see +/// #ikarus_project_get_error_message. +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief Delineates what caused an error. +/// \details First 2 bytes delineate the major type, next 2 bytes delineate the minor type, next 4 bytes delineate the detail +/// type. +/// \remark Note that this doesn't show responsibility. An error with source "SubSystem" could still be the fault of +/// libikarus. +enum IkarusErrorInfo { + /// \brief No error occurred. + IkarusErrorInfo_Source_None = 0x0001000000000000, + /// \brief The error was caused by the client. + IkarusErrorInfo_Source_Client = 0x0001000000000001, + /// \brief The error was caused by a sub-system of libikarus. + IkarusErrorInfo_Source_SubSystem = 0x0001000000000002, + /// \brief The error was caused by libikarus itself. + IkarusErrorInfo_Source_LibIkarus = 0x0001000000000003, + /// \brief The error was caused by an unknown source. + IkarusErrorInfo_Source_Unknown = 0x00010000FFFFFFFF, + /// \brief No error occurred. + IkarusErrorInfo_Type_None = 0x0002000000000000, + /// \brief The user misused the API. + /// \example Accessing a resource that does not exist. + IkarusErrorInfo_Type_Client_Misuse = 0x0002000100000001, + /// \brief The user provided invalid input. + /// \example Passing null for a pointer that must not be null. + IkarusErrorInfo_Type_Client_Input = 0x0002000100000002, + /// \brief An error occurred while interacting with a dependency from ikarus. + /// \example An error occurred in the underlying OS library. + IkarusErrorInfo_Type_SubSystem_Dependency = 0x0002000200000001, + /// \brief An error occurred while interacting with the database. + /// \example An error occurred while executing a query. + IkarusErrorInfo_Type_SubSystem_Database = 0x0002000200000002, + /// \brief An error occurred while interacting with the filesystem. + /// \example An error occurred while reading a file. + IkarusErrorInfo_Type_SubSystem_Filesystem = 0x0002000200000003, + /// \brief A datapoint within ikarus is invalid for the current state of the system. + /// \example The name of an object is found to be invalid UTF8. + IkarusErrorInfo_Type_LibIkarus_InvalidState = 0x0002000300000001, + /// \brief LibIkarus is unable to perform a certain operation that should succeed. + /// \example Migrating a project fails + IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation = 0x0002000300000002, + /// \brief LibIkarus is unable to perform a certain operation within a given timeframe. + /// \example A query takes longer than the timeout. + IkarusErrorInfo_Type_LibIkarus_Timeout = 0x0002000300000003, + /// \brief The type of error is unknown. + IkarusErrorInfo_Type_Unknown = 0xFFFFFFFF, +}; + +/// \brief Gets the name of an error info. +/// \param info The error info to get the name of. +/// \return The name of the error info. +/// \remark The returned pointer is valid for the lifetime of the program and must not be freed. +IKA_API char const * get_error_info_name(IkarusErrorInfo info); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/global.h b/include/ikarus/global.h index 6d91227..1572b24 100644 --- a/include/ikarus/global.h +++ b/include/ikarus/global.h @@ -1,6 +1,6 @@ #pragma once -/// \file memory.h +/// \file global.h /// \author Folling #include @@ -9,8 +9,12 @@ /// \brief Information relevant to the entire library. /// @{ +IKARUS_BEGIN_HEADER + /// \brief Frees a pointer allocated by ikarus. Every pointer returned by a function must be freed using this function unless /// explicitly stated otherwise. IKA_API void ikarus_free(void * ptr); +IKARUS_END_HEADER + /// @} diff --git a/src/errors.cpp b/src/errors.cpp new file mode 100644 index 0000000..19c3bf9 --- /dev/null +++ b/src/errors.cpp @@ -0,0 +1,22 @@ +#include "ikarus/errors.h" + +char const * get_error_info_name(IkarusErrorInfo info) { + switch (info) { + case IkarusErrorInfo_Source_None: return "IkarusErrorSource_None"; + case IkarusErrorInfo_Source_Client: return "IkarusErrorSource_Client"; + case IkarusErrorInfo_Source_SubSystem: return "IkarusErrorSource_SubSystem"; + case IkarusErrorInfo_Source_LibIkarus: return "IkarusErrorSource_LibIkarus"; + case IkarusErrorInfo_Source_Unknown: return "IkarusErrorSource_Unknown"; + case IkarusErrorInfo_Type_None: return "IkarusErrorType_None"; + case IkarusErrorInfo_Type_Client_Misuse: return "IkarusErrorType_Client_Misuse"; + case IkarusErrorInfo_Type_Client_Input: return "IkarusErrorType_Client_Input"; + case IkarusErrorInfo_Type_SubSystem_Dependency: return "IkarusErrorType_SubSystem_Dependency"; + case IkarusErrorInfo_Type_SubSystem_Database: return "IkarusErrorType_SubSystem_Database"; + case IkarusErrorInfo_Type_SubSystem_Filesystem: return "IkarusErrorType_SubSystem_Filesystem"; + case IkarusErrorInfo_Type_LibIkarus_InvalidState: return "IkarusErrorType_LibIkarus_InvalidState"; + case IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation: return "IkarusErrorType_LibIkarus_CannotPerformOperation"; + case IkarusErrorInfo_Type_LibIkarus_Timeout: return "IkarusErrorType_LibIkarus_Timeout"; + case IkarusErrorInfo_Type_Unknown: return "IkarusErrorType_Unknown"; + default: return "Unknown"; + } +} diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index 3d1c997..63ce726 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -21,39 +21,56 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - LOG_VERBOSE("project={}; name={}", project->path.c_str(), name); + auto ctx = project->function_context(); if (name == nullptr) { - LOG_ERROR("name is nullptr"); + ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return nullptr; } if (cppbase::is_empty_or_blank(name)) { - LOG_ERROR("name is empty or blank"); + ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return nullptr; } - VTRYRV(auto id, nullptr, project->db->transact([name](auto * db) -> cppbase::Result { - LOG_VERBOSE("creating blueprint in objects table"); + LOG_VERBOSE("project={}; name={}", project->path().c_str(), name); - TRY(db->execute( - "INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(IkarusObjectType_Blueprint), name - )); + VTRYRV( + auto id, + nullptr, + project->db() + ->transact([name](auto * db) -> cppbase::Result { + LOG_VERBOSE("creating blueprint in objects table"); - auto id = db->last_insert_rowid(); + TRY(db->execute( + "INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", + static_cast(IkarusObjectType_Blueprint), + name + )); - LOG_VERBOSE("blueprint is {}", id); + auto id = db->last_insert_rowid(); - LOG_VERBOSE("inserting blueprint into blueprints table"); + LOG_VERBOSE("id is {}", id); - TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?);", id)); + LOG_VERBOSE("inserting blueprint into blueprints table"); - return cppbase::ok(id); - })); + TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?);", id)); + + return cppbase::ok(id); + }) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("unable to insert blueprint into database: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); LOG_VERBOSE("successfully created blueprint"); - return new IkarusBlueprint{project, id}; + return project->get_blueprint(id); } void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { @@ -64,16 +81,27 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { return; } + auto * ctx = blueprint->project->function_context(); + LOG_VERBOSE("blueprint={}", blueprint->id); - if (auto res = blueprint->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id); res.is_err()) { - LOG_ERROR("failed to delete blueprint {} from objects table: {}", blueprint->id, res.unwrap_error()); - return; - } + TRYRV( + , + blueprint->project->db() + ->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to delete blueprint from objects table: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); - LOG_VERBOSE("blueprint was successfully deleted from database, freeing pointer"); + LOG_VERBOSE("blueprint was successfully deleted from database, freeing blueprint"); - delete blueprint; + blueprint->project->remove_blueprint(blueprint); LOG_VERBOSE("successfully deleted blueprint"); } @@ -86,14 +114,23 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { return 0; } + auto * ctx = blueprint->project->function_context(); + LOG_VERBOSE("blueprint={}", blueprint->id); VTRYRV( auto count, 0, - blueprint->project->db->query_one( - "SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint_id` = ?;", blueprint->id - ) + blueprint->project->db() + ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint_id` = ?;", blueprint->id) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint property count: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) ); return static_cast(count); @@ -109,6 +146,8 @@ void ikarus_blueprint_get_properties( return; } + auto * ctx = blueprint->project->function_context(); + if (properties_out == nullptr) { LOG_ERROR("properties_out is nullptr"); return; @@ -118,17 +157,28 @@ void ikarus_blueprint_get_properties( IkarusId ids[properties_out_size]; - if (auto res = blueprint->project->db->query_many_buffered( - "SELECT `id` FROM `properties` WHERE `source` = ?", static_cast(ids), properties_out_size, blueprint->id - ); - res.is_err()) { - LOG_ERROR("failed to fetch blueprint property ids: {}", res.unwrap_error()); - return; - } + TRYRV( + , + blueprint->project->db() + ->query_many_buffered( + "SELECT `id` FROM `properties` WHERE `source` = ?", + static_cast(ids), + properties_out_size, + blueprint->id + ) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint property ids: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); for (size_t i = 0; i < properties_out_size; ++i) { /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - properties_out[i] = new IkarusProperty{blueprint->project, ids[i]}; + properties_out[i] = blueprint->project->get_property(ids[i]); } LOG_VERBOSE("successfully fetched blueprint properties"); diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index 91b3d3e..28fb068 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -3,4 +3,7 @@ #include /// \private -struct IkarusEntity : public IkarusObject {}; +struct IkarusEntity : public IkarusObject { + inline IkarusEntity(struct IkarusProject * project, IkarusId id): + IkarusObject{project, id} {} +}; diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index db52508..5cbea00 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -1,11 +1,148 @@ +#pragma once + +#include #include +#include +#include #include #include +#include + +constexpr inline size_t MAXIMUM_ERROR_INFOS = 8; +constexpr inline size_t MAXIMUM_ERROR_MESSAGE_LENGTH = 256; + +class FunctionContext { +public: + explicit FunctionContext(struct IkarusProject * project); + FunctionContext(FunctionContext const&) noexcept = default; + FunctionContext(FunctionContext&&) noexcept = default; + + auto operator=(FunctionContext const&) noexcept -> FunctionContext& = default; + auto operator=(FunctionContext&&) noexcept -> FunctionContext& = default; + + ~FunctionContext(); + + template + requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) + auto set_error(std::string_view error_message, bool log_error, Infos... infos) -> void; + +private: + struct IkarusProject * _project; +}; + /// \private struct IkarusProject { - std::string name; - std::filesystem::path path; - std::unique_ptr db; +public: + [[nodiscard]] inline auto name() const -> std::string_view { + return _name; + } + + [[nodiscard]] inline auto path() const -> std::filesystem::path const& { + return _path; + } + + [[nodiscard]] inline auto db() -> sqlitecpp::Connection * { + return _db.get(); + } + + inline auto function_context() -> FunctionContext * { + return &_function_contexts.emplace(this); + } + + [[nodiscard]] IkarusBlueprint * get_blueprint(IkarusId id) { + return get_cached_object(id, this->_blueprints); + } + + auto remove_blueprint(IkarusBlueprint * blueprint) -> void { + remove_cached_object(blueprint, _blueprints); + } + + [[nodiscard]] auto get_entity(IkarusId id) -> IkarusEntity * { + return get_cached_object(id, this->_entities); + } + + auto remove_entity(IkarusEntity * entity) -> void { + remove_cached_object(entity, _entities); + } + + [[nodiscard]] auto get_property(IkarusId id) -> IkarusProperty * { + return get_cached_object(id, this->_properties); + } + + auto remove_property(IkarusProperty * property) -> void { + remove_cached_object(property, _properties); + } + +private: + template + [[nodiscard]] T * get_cached_object(IkarusId id, std::unordered_map>& cache) { + if (auto iter = cache.find(id); iter == cache.cend()) { + auto [ret_iter, _] = cache.emplace(id, std::make_unique(this, id)); + + return ret_iter->second.get(); + } else { + return iter->second.get(); + } + } + + template + void remove_cached_object(T * object, std::unordered_map>& cache) { + cache.erase(object->id); + } + +private: + friend class FunctionContext; + + std::string _name; + std::filesystem::path _path; + std::unique_ptr _db; + + std::array error_infos; + std::string error_message_buffer; + + std::unordered_map> _blueprints; + std::unordered_map> _properties; + std::unordered_map> _entities; + + std::stack _function_contexts; }; + +FunctionContext::FunctionContext(struct IkarusProject * project): + _project{project} {} + +FunctionContext::~FunctionContext() { + if (_project->_function_contexts.size() == 1) { + if (_project->error_message_buffer.empty()) { + _project->error_message_buffer.push_back('\0'); + } else { + _project->error_message_buffer[0] = '\0'; + } + + _project->error_infos = {}; + } + + _project->_function_contexts.pop(); +} + +template + requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) +auto FunctionContext::set_error(std::string_view error_message, bool log_error, Infos... infos) { + if (error_message.size() > _project->error_message_buffer.size()) { + _project->error_message_buffer.resize(error_message.size() + 1); + } + + for (int i = 0; i < error_message.size(); ++i) { + _project->error_message_buffer[i] = error_message[i]; + } + + _project->error_message_buffer[error_message.size()] = '\0'; + _project->error_infos = {infos...}; + + if (log_error) { + LOG_ERROR( + "Error({}): {}", fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), error_message + ); + } +} From a791dccfd91d25c4cba68ac51ee196a89116e8a8 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 21 Nov 2023 15:10:43 +0100 Subject: [PATCH 096/166] split property & values into separate classes and files Signed-off-by: Folling --- implementation_details | 12 -- include/ikarus/objects/blueprint.h | 5 +- include/ikarus/objects/entity.h | 39 +++--- include/ikarus/objects/object.h | 12 ++ include/ikarus/objects/object_type.h | 3 - .../objects/properties/number_property.h | 25 ++++ .../objects/{ => properties}/property.h | 81 ++++++----- .../{ => properties}/property_source.h | 16 ++- .../objects/{ => properties}/property_type.h | 2 - .../settings/number_property_settings.h | 31 ++++ .../properties/settings/property_settings.h | 92 ++++++++++++ .../settings/text_property_settings.h | 31 ++++ .../settings/toggle_property_settings.h | 31 ++++ .../ikarus/objects/properties/text_property.h | 25 ++++ .../objects/properties/toggle_property.h | 25 ++++ include/ikarus/objects/property_type_info.h | 79 ----------- include/ikarus/values/number_value.h | 44 ++++++ include/ikarus/values/text_value.h | 44 ++++++ include/ikarus/values/toggle_value.h | 44 ++++++ include/ikarus/values/value.h | 132 ++++-------------- src/objects/blueprint.cpp | 3 - src/objects/property.cpp | 87 ++++++++++++ src/objects/property_info.hpp | 74 ++++++++++ src/objects/property_source.hpp | 10 ++ src/persistence/project.hpp | 3 +- src/values/number_value.hpp | 5 + src/values/text_value.hpp | 8 ++ src/values/toggle_value.hpp | 6 + 28 files changed, 700 insertions(+), 269 deletions(-) delete mode 100644 implementation_details create mode 100644 include/ikarus/objects/properties/number_property.h rename include/ikarus/objects/{ => properties}/property.h (62%) rename include/ikarus/objects/{ => properties}/property_source.h (69%) rename include/ikarus/objects/{ => properties}/property_type.h (96%) create mode 100644 include/ikarus/objects/properties/settings/number_property_settings.h create mode 100644 include/ikarus/objects/properties/settings/property_settings.h create mode 100644 include/ikarus/objects/properties/settings/text_property_settings.h create mode 100644 include/ikarus/objects/properties/settings/toggle_property_settings.h create mode 100644 include/ikarus/objects/properties/text_property.h create mode 100644 include/ikarus/objects/properties/toggle_property.h delete mode 100644 include/ikarus/objects/property_type_info.h create mode 100644 include/ikarus/values/number_value.h create mode 100644 include/ikarus/values/text_value.h create mode 100644 include/ikarus/values/toggle_value.h create mode 100644 src/objects/property.cpp create mode 100644 src/objects/property_info.hpp create mode 100644 src/objects/property_source.hpp create mode 100644 src/values/number_value.hpp create mode 100644 src/values/text_value.hpp create mode 100644 src/values/toggle_value.hpp diff --git a/implementation_details b/implementation_details deleted file mode 100644 index 42b38fc..0000000 --- a/implementation_details +++ /dev/null @@ -1,12 +0,0 @@ -This list is intended to help keep the documentation up to date. -If you make changes to, for example, templates, always check the documentation for templates. -But sometimes information is shared. and referenced in multiple places. This helps keep track of that. - -Usage: Search for these keys prefixed with IMPLEMENTATION_DETAIL_* to change documentation in relevant places. - -DATABASE: References to the usage of a database -TREE_LAYOUT: References to our usage of a tree layout -OBJECT_TYPES: References to the types of objects -OBJECT_SCOPES: References to the usage of object scopes -PROPERTY_TYPES: The property types that currently exist -LAZY_VALUE_CREATION: The fact that values are created lazily \ No newline at end of file diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 85f40eb..57d1a7c 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -77,7 +77,10 @@ IKA_API void ikarus_blueprint_get_linked_entities( /// \pre \li Must exist. /// \return The blueprint represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint const * blueprint); +IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint); + +/// \see ikarus_blueprint_to_object +IKA_API struct IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 813adea..2ac9087 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -50,12 +50,7 @@ struct IkarusEntity; /// \return The created entity or null if an error occurs. /// \remark Must be freed using #ikarus_free. IKA_API IkarusEntity * ikarus_entity_create( - struct IkarusProject * project, - struct IkarusEntityFolder * parent, - size_t position, - char const * name, - struct IkarusBlueprint ** blueprints, - size_t blueprints_count + struct IkarusProject * project, char const * name, struct IkarusBlueprint ** blueprints, size_t blueprints_count ); /// \brief Deletes an entity. @@ -67,16 +62,6 @@ IKA_API void ikarus_entity_delete(IkarusEntity * entity); IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint); -/// \brief Checks if an entity has a specific property. -/// \param entity The entity to check. -/// \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 exist. -/// \return True if the entity has the property, false otherwise. -IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property); - /// \brief Links an entity to a blueprint. /// \param entity The entity to link. /// \pre \li Must not be null. @@ -98,6 +83,16 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru /// \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); +/// \brief Checks if an entity has a specific property. +/// \param entity The entity to check. +/// \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 exist. +/// \return True if the entity has the property, false otherwise. +IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property); + /// \brief Gets the number of properties of an entity. /// \param entity The entity to get the number of properties of. /// \pre \li Must not be null. @@ -126,7 +121,7 @@ IKA_API void ikarus_entity_get_properties( /// \pre \li Must exist. /// \return The value of the property or null if the entity does not have the property or an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusEntityValue * get_value(IkarusEntity const * entity, struct IkarusProperty const * property); +IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property); /// \brief Sets the value of a property of an entity. /// \param entity The entity to set the value of. @@ -138,11 +133,10 @@ IKA_API struct IkarusEntityValue * get_value(IkarusEntity const * entity, struct /// \param value The new value of the property. /// \pre \li Must not be null. /// \pre \li Must be of the same type as the property. -/// \param validate_settings If set, this function fails not only if the type of the value is invalid, but also if it's not -/// valid under the properties settings. \see property.h +/// \pre \li Must be valid for the property's settings. /// \remark If the entity does not have the property, this function fails. IKA_API void ikarus_entity_set_value( - IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue * value, bool validate_settings + IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value ); /// \brief Casts an entity to an object. @@ -151,7 +145,10 @@ IKA_API void ikarus_entity_set_value( /// \pre \li Must exist. /// \return The entity represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity const * entity); +IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity); + +/// \see ikarus_entity_to_object +IKA_API struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity); IKARUS_END_HEADER diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index 256e8a0..a2cce6a 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -41,6 +41,18 @@ IKA_API void ikarus_object_visit( void * data ); +/// \see ikarus_object_visit +IKA_API void ikarus_object_visit_const( + IkarusObject const * object, + void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), + void (*property_visitor)(struct IkarusProperty const *, void *), + void (*entity_visitor)(struct IkarusEntity const *, void *), + void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder const *, void *), + void (*property_folder_visitor)(struct IkarusPropertyFolder const *, void *), + void (*entity_folder_visitor)(struct IkarusEntityFolder const *, void *), + void * data +); + IKARUS_END_HEADER // @} diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 73f724b..2539e2d 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -10,10 +10,7 @@ IKARUS_BEGIN_HEADER -// IMPLEMENTATION_DETAIL_OBJECT_TYPES - /// \brief The type of an object. -/// \remark The folder types are identical to their counterparts in #IkarusFolderType. enum IkarusObjectType { /// \brief Not an object or no object. IkarusObjectType_None = 0, diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h new file mode 100644 index 0000000..9045140 --- /dev/null +++ b/include/ikarus/objects/properties/number_property.h @@ -0,0 +1,25 @@ +#pragma once + +/// \file number_property.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// \brief Number properties store a numeric value. (e.g. "Weight" or "Age") +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusNumberProperty; + +IKA_API IkarusNumberProperty * ikarus_number_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + struct IkarusNumberSettings * settings +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/properties/property.h similarity index 62% rename from include/ikarus/objects/property.h rename to include/ikarus/objects/properties/property.h index a62a821..f1ebf49 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/properties/property.h @@ -1,12 +1,10 @@ #pragma once -// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION - /// \file property.h /// \author Folling #include -#include +#include #include /// \defgroup properties Properties @@ -52,33 +50,12 @@ IKARUS_BEGIN_HEADER /// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. struct IkarusProperty; -/// \brief Creates a property -/// \param project The project the property is part of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param property_source The property source the property is part of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param name The name of the property. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param property_info The info of the property. -/// \pre \li Must not be null. -/// \return The created property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusProperty * ikarus_property_create( - struct IkarusProject * project, - struct IkarusPropertySource * property_source, - char const * name, - struct IkarusPropertyTypeInfo * property_info -); - /// \brief Deletes a property. /// \param property The property to delete. /// \pre \li Must not be null. /// \pre \li Must exist. /// \remark The property must not be accessed after deletion. -IKA_API void ikarus_property_delete(struct IkarusProperty * property); +IKA_API void ikarus_property_delete(IkarusProperty * property); /// \brief Gets the type info of a property. /// \param property The property to get the type info of. @@ -86,7 +63,18 @@ IKA_API void ikarus_property_delete(struct IkarusProperty * property); /// \pre \li Must exist. /// \return The type info of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyTypeInfo * ikarus_property_get_type_info(IkarusProperty const * property); +IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property); + +/// \brief Gets the settings of a property. +/// \param property The property to get the settings of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The settings of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusPropertySettings * ikarus_property_get_settings(IkarusProperty * property); + +/// \see ikarus_property_get_settings +IKA_API struct IkarusPropertySettings const * ikarus_property_get_settings_const(IkarusProperty const * property); /// \brief Gets the source of a property. /// \param property The property to get the source of. @@ -94,25 +82,31 @@ IKA_API struct IkarusPropertyTypeInfo * ikarus_property_get_type_info(IkarusProp /// \pre \li Must exist. /// \return The source of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * ikarus_property_get_source(IkarusProperty const * property); +IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property); -/// \brief Gets the default value of a property. -/// \param property The property to get the default value of. +/// \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. -/// \return The default value of the property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); +/// \param toggle_property The function to call if the property is a toggle property. Skipped if null. +/// \param number_property The function to call if the property is a number property. Skipped if null. +/// \param text_property The function to call if the property is a text property. Skipped if null. +/// \param data The data to pass to the functions. +IKA_API void ikarus_property_visit( + IkarusProperty * property, + void (*toggle_property)(struct IkarusToggleProperty *, void *), + void (*number_property)(struct IkarusNumberProperty *, void *), + void (*text_property)(struct IkarusTextProperty *, void *), + void * data +); -/// \brief Sets the type info of a property and resets all values to the new default value. -/// \param property The property to set the type info of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_type The new type info of the property. -/// \param attempt_conversion Whether to attempt to convert the property's values to the new type info. Conversion rules are -/// unspecified for now, but follow common sense. -IKA_API void ikarus_property_set_type_info( - IkarusProperty * property, struct IkarusPropertyTypeInfo new_type_info, bool attempt_conversion +/// \see ikarus_property_visit +IKA_API void ikarus_property_visit_const( + IkarusProperty const * property, + void (*toggle_property)(struct IkarusToggleProperty const *, void *), + void (*number_property)(struct IkarusNumberProperty const *, void *), + void (*text_property)(struct IkarusTextProperty const *, void *), + void * data ); /// \brief Casts a property to an object. @@ -121,7 +115,10 @@ IKA_API void ikarus_property_set_type_info( /// \pre \li Must exist. /// \return The property represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty const * property); +IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty * property); + +/// \see ikarus_property_to_object +IKA_API struct IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property); IKARUS_END_HEADER diff --git a/include/ikarus/objects/property_source.h b/include/ikarus/objects/properties/property_source.h similarity index 69% rename from include/ikarus/objects/property_source.h rename to include/ikarus/objects/properties/property_source.h index 4dbcd47..275baa4 100644 --- a/include/ikarus/objects/property_source.h +++ b/include/ikarus/objects/properties/property_source.h @@ -10,7 +10,7 @@ IKARUS_BEGIN_HEADER -struct PropertySource; +struct IkarusPropertySource; /// \brief Creates an blueprint property source. /// \param blueprint The blueprint to create the property source for. @@ -18,7 +18,7 @@ struct PropertySource; /// \pre \li Must exist. /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct PropertySource * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint); +IKA_API struct IkarusPropertySource * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint); /// \brief Creates an entity property source. /// \param entity The entity to create the property source for. @@ -26,7 +26,7 @@ IKA_API struct PropertySource * ikarus_property_source_create_blueprint(struct I /// \pre \li Must exist. /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct PropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity); +IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity); /// \brief Visits a property source, calling the appropriate callback. /// \param property_source The property source to visit. @@ -36,12 +36,20 @@ IKA_API struct PropertySource * ikarus_property_source_create_entity(struct Ikar /// \param entity_visitor The callback to call if the source is an entity, skipped if null. /// \param user_data User data to pass to the callbacks. IKA_API void ikarus_property_source_visit( - struct PropertySource * property_source, + struct IkarusPropertySource * property_source, void (*blueprint_visitor)(struct IkarusBlueprint *, void *), void (*entity_visitor)(struct IkarusEntity *, void *), void * user_data ); +/// \see ikarus_property_source_visit +IKA_API void ikarus_property_source_visit_const( + struct IkarusPropertySource const * property_source, + void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), + void (*entity_visitor)(struct IkarusEntity const *, void *), + void * user_data +); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/objects/property_type.h b/include/ikarus/objects/properties/property_type.h similarity index 96% rename from include/ikarus/objects/property_type.h rename to include/ikarus/objects/properties/property_type.h index e26b1f0..89e5fa1 100644 --- a/include/ikarus/objects/property_type.h +++ b/include/ikarus/objects/properties/property_type.h @@ -1,7 +1,5 @@ #pragma once -// IMPLEMENTATION_DETAIL_PROPERTY_TYPES - /// \file property_type.h /// \author Folling diff --git a/include/ikarus/objects/properties/settings/number_property_settings.h b/include/ikarus/objects/properties/settings/number_property_settings.h new file mode 100644 index 0000000..fcdaee8 --- /dev/null +++ b/include/ikarus/objects/properties/settings/number_property_settings.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file number_property_settings.h +/// \author Folling + +#include + +/// \addtogroup property_settings PropertySettings +/// \brief Number property settings add additional constraints to number properties. +/// \details The following settings are available: +/// +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusNumberPropertySettings; + +/// \brief Sets the default value for a number property. +/// \param settings The number property settings. +/// \pre \li Must not be null. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API void ikarus_number_property_settings_set_default_value( + struct IkarusNumberPropertySettings * settings, struct IkarusNumberValue * default_value +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/properties/settings/property_settings.h b/include/ikarus/objects/properties/settings/property_settings.h new file mode 100644 index 0000000..359097c --- /dev/null +++ b/include/ikarus/objects/properties/settings/property_settings.h @@ -0,0 +1,92 @@ +#pragma once + +/// \file property_settings.h +/// \author Folling + +#include + +/// \defgroup property_settings PropertySettings +/// \brief Property settings add additional constraints to properties. +/// \details Each property has a certain set of settings. The options available depend on the type of the property. +/// Settings can be changed after the property has been created. Examples of settings are: +/// Note that the default value must be set using the concrete subtypes to ascertain type correctness. +/// - Minimum: The minimum value for a number property. +/// - Matches: A regular expression that the value of a text property must match. +/// There are also some common settings, shared among all properties: +/// - Default Value (default: PropertyType's default default (sic) value): The value that is returned if no value is specified +/// for some entity. +/// - List (default: false): If set to true, the property becomes a list. Instead of one number, you could then specify a series +/// of numbers. Note that each element in the list is subject to changes in values. E.g. when we say that all values are changed +/// in some way (e.g. reset to the default value), this applies to all elements in the list. +/// - Allow undefined (default: false): If set to true, you can specify an "unknown" value for a property. +/// It might not be known if a character is dead or not for example. +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusPropertySettings; + +/// \brief Gets the default value of a property. +/// \param settings The settings to get the default value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The default value of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusValue const * ikarus_property_settings_get_default_value(IkarusPropertySettings const * settings); + +/// \brief Fetches whether a property is a list. +/// \param settings The property settings. +/// \pre \li Must not be null. +/// \return True if the property is a list, false otherwise. +IKA_API bool ikarus_property_settings_is_list(IkarusPropertySettings const * settings); + +/// \brief Sets whether a property is a list. +/// \details Noop if unchanged. A change from list -> single truncates to just the first element. A change from single -> list +/// sets the first element to the current value. +/// \param settings The property settings. +/// \pre \li Must not be null. +/// \param list Whether the property should be +/// a list. +IKA_API void ikarus_property_settings_set_is_list(IkarusPropertySettings * settings, bool list); + +/// \brief Fetches whether a property may be undefined. +/// \param settings The property settings. +/// \pre \li Must not be null. +/// \return True if the property may be undefined, false otherwise. +IKA_API bool ikarus_property_settings_may_be_undefined(IkarusPropertySettings const * settings); + +/// \brief Sets whether a property may be undefined. +/// \details Noop if unchanged. If the transition is from undefined -> defined, all undefined values will be reset to the +/// default value. +/// \param settings The property settings. +/// \pre \li Must not be null. +/// \param allow_undefined Whether the property should be allowed to be undefined. +/// \param allow_undefined Whether the property should be allowed to be undefined. +IKA_API void ikarus_property_settings_set_may_be_undefined(IkarusPropertySettings * settings, bool allow_undefined); + +/// \brief Visits a property settings. Calling the appropriate function for the property's type. +/// \param settings The property settings. +/// \pre \li Must not be null. +/// \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 Data passed to the visitors. +IKA_API void ikarus_property_settings_visit( + struct IkarusPropertySettings * settings, + void (*toggle_property_visitor)(struct IkarusTogglePropertySettings * settings, void * data), + void (*number_property_visitor)(struct IkarusNumberPropertySettings * settings, void * data), + void (*text_property_visitor)(struct IkarusTextPropertySettings * settings, void * data), + void * data +); + +IKA_API void ikarus_property_settings_visit_const( + struct IkarusPropertySettings const * settings, + void (*toggle_property_visitor)(struct IkarusTogglePropertySettings const * settings, void * data), + void (*number_property_visitor)(struct IkarusNumberPropertySettings const * settings, void * data), + void (*text_property_visitor)(struct IkarusTextPropertySettings const * settings, void * data), + void * data +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/properties/settings/text_property_settings.h b/include/ikarus/objects/properties/settings/text_property_settings.h new file mode 100644 index 0000000..926058d --- /dev/null +++ b/include/ikarus/objects/properties/settings/text_property_settings.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file text_property_settings.h +/// \author Folling + +#include + +/// \addtogroup property_settings PropertySettings +/// \brief Text property settings add additional constraints to text properties. +/// \details The following settings are available: +/// +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusTextPropertySettings; + +/// \brief Sets the default value for a text property. +/// \param settings The text property settings. +/// \pre \li Must not be null. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API void ikarus_text_property_settings_set_default_value( + struct IkarusTextPropertySettings * settings, struct IkarusTextValue * default_value +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/properties/settings/toggle_property_settings.h b/include/ikarus/objects/properties/settings/toggle_property_settings.h new file mode 100644 index 0000000..d372929 --- /dev/null +++ b/include/ikarus/objects/properties/settings/toggle_property_settings.h @@ -0,0 +1,31 @@ +#pragma once + +/// \file toggle_property_settings.h +/// \author Folling + +#include + +/// \addtogroup property_settings PropertySettings +/// \brief Toggle property settings add additional constraints to toggle properties. +/// \details The following settings are available: +/// +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusTogglePropertySettings; + +/// \brief Sets the default value for a toggle property. +/// \param settings The toggle property settings. +/// \pre \li Must not be null. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API void ikarus_toggle_property_settings_set_default_value( + struct IkarusTogglePropertySettings * settings, struct IkarusToggleValue * default_value +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h new file mode 100644 index 0000000..f7975b3 --- /dev/null +++ b/include/ikarus/objects/properties/text_property.h @@ -0,0 +1,25 @@ +#pragma once + +/// \file text_property.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// \brief Text properties store an arbitrary piece of text. (e.g. "Firstname" or "Description") +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusTextProperty; + +IKA_API IkarusTextProperty * ikarus_text_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + struct IkarusTextSettings * settings +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h new file mode 100644 index 0000000..baa0801 --- /dev/null +++ b/include/ikarus/objects/properties/toggle_property.h @@ -0,0 +1,25 @@ +#pragma once + +/// \file toggle_property.h +/// \author Folling + +#include + +/// \addtogroup properties Properties +/// \brief Toggle properties store a value that can be either true or false. (e.g. "Is the character dead?") +/// @{ + +IKARUS_BEGIN_HEADER + +struct IkarusToggleProperty; + +IKA_API IkarusToggleProperty * ikarus_toggle_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + struct IkarusToggleSettings * settings +); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/property_type_info.h b/include/ikarus/objects/property_type_info.h deleted file mode 100644 index cd7b6b9..0000000 --- a/include/ikarus/objects/property_type_info.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -/// \file property_info.h -/// \author Folling - -#include - -/// \addtogroup properties Properties -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief Information about a property. -/// \details Property information includes their type and settings consolidated to ascertain type safety. -struct IkarusPropertyTypeInfo; - -/// \brief Information about a toggle property. -struct IkarusTogglePropertyInfo; - -/// \brief Information about a number property. -struct IkarusNumberPropertyInfo; - -/// \brief Information about a text property. -struct IkarusTextPropertyInfo; - -/// \brief Creates a new toggle property info. -/// \return The created toggle property info. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTogglePropertyInfo * ikarus_toggle_property_info_create(); -/// \brief Sets the default value of a toggle property info. -/// \param toggle_property_info The toggle property info to set the default value of. -/// \pre \li Must not be null. -/// \param default_value The default value to set. -/// \pre \li Must not be null. -IKA_API void ikarus_toggle_property_info_set_default_value( - IkarusTogglePropertyInfo * toggle_property_info, struct IkarusToggleValue * default_value -); -/// \brief Converts a toggle property info to a generic property info. -/// \param toggle_property_info The toggle property info to convert. -/// \return The converted property info. -IKA_API IkarusPropertyTypeInfo * ikarus_toggle_property_info_to_property_info(IkarusTogglePropertyInfo * toggle_property_info); - -/// \brief Creates a new number property info. -/// \return The created number property info. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberPropertyInfo * ikarus_number_property_info_create(); -/// \brief Sets the default value of a number property info. -/// \param number_property_info The number property info to set the default value of. -/// \pre \li Must not be null. -/// \param default_value The default value to set. -/// \pre \li Must not be null. -IKA_API void ikarus_number_property_info_set_default_value( - IkarusNumberPropertyInfo * number_property_info, struct IkarusNumberValue * default_value -); -/// \brief Converts a number property info to a generic property info. -/// \param number_property_info The number property info to convert. -/// \return The converted property info. -IKA_API IkarusPropertyTypeInfo * ikarus_number_property_info_to_property_info(IkarusNumberPropertyInfo * number_property_info); - -/// \brief Creates a new text property info. -/// \return The created text property info. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextPropertyInfo * ikarus_text_property_info_create(); -/// \brief Sets the default value of a text property info. -/// \param text_property_info The text property info to set the default value of. -/// \pre \li Must not be null. -/// \param default_value The default value to set. -/// \pre \li Must not be null. -IKA_API void ikarus_text_property_info_set_default_value( - IkarusTextPropertyInfo * text_property_info, struct IkarusTextValue * default_value -); -/// \brief Converts a text property info to a generic property info. -/// \param text_property_info The text property info to convert. -/// \return The converted property info. -IKA_API IkarusPropertyTypeInfo * ikarus_text_property_info_to_property_info(IkarusTextPropertyInfo * text_property_info); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h new file mode 100644 index 0000000..2c054f8 --- /dev/null +++ b/include/ikarus/values/number_value.h @@ -0,0 +1,44 @@ +#pragma once + +/// \file number_value.h +/// \author Folling + +#include + +/// \addtogroup entity_value Entity Values +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A true/false boolean-like value. For example "IsDead". +struct IkarusNumberValue; + +/// \brief Creates a number value from a long double. +/// \param value The numeric value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberValue * ikarus_number_value_create(long double value); + +/// \brief Creates an indeterminate number value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); + +/// \brief Sets the value of a number value. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); + +/// \brief Fetches the underlying value of a number value. +/// \param value The number value. +/// \return The underlying value. +/// \warning If the value is indeterminate, false is returned. +IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value); + +/// \brief Converts a number value to an entity value. +/// \param number_value The number value to convert. +/// \return The converted entity value. +IKA_API struct IkarusEntityValue * ikarus_number_value_to_entity_value(IkarusNumberValue * number_value); + +IKARUS_END_HEADER diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h new file mode 100644 index 0000000..867a638 --- /dev/null +++ b/include/ikarus/values/text_value.h @@ -0,0 +1,44 @@ +#pragma once + +/// \file text_value.h +/// \author Folling + +#include + +/// \addtogroup entity_value Entity Values +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A true/false boolean-like value. For example "IsDead". +struct IkarusTextValue; + +/// \brief Creates a text value from a boolean. +/// \param value The text value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextValue * ikarus_text_value_create(char const * value); + +/// \brief Creates an indeterminate text value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); + +/// \brief Sets the value of a text value. +/// \param value The text value. +/// \pre \li Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); + +/// \brief Fetches the underlying value of a text value. +/// \param value The text value. +/// \return The underlying value. +/// \warning If the value is indeterminate, false is returned. +IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); + +/// \brief Converts a text value to an entity value. +/// \param text_value The text value to convert. +/// \return The converted entity value. +IKA_API struct IkarusEntityValue * ikarus_text_value_to_entity_value(IkarusTextValue * text_value); + +IKARUS_END_HEADER diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h new file mode 100644 index 0000000..7c77870 --- /dev/null +++ b/include/ikarus/values/toggle_value.h @@ -0,0 +1,44 @@ +#pragma once + +/// \file toggle_value.h +/// \author Folling + +#include + +/// \addtogroup entity_value Entity Values +/// @{ + +IKARUS_BEGIN_HEADER + +/// \brief A true/false boolean-like value. For example "IsDead". +struct IkarusToggleValue; + +/// \brief Creates a toggle value from a boolean. +/// \param value The toggle value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value); + +/// \brief Creates an indeterminate toggle value. +/// \return The entity value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); + +/// \brief Sets the value of a toggle value. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \param new_value The new value. +IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); + +/// \brief Fetches the underlying value of a toggle value. +/// \param value The toggle value. +/// \return The underlying value. +/// \warning If the value is indeterminate, false is returned. +IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value); + +/// \brief Converts a toggle value to an entity value. +/// \param toggle_value The toggle value to convert. +/// \return The converted entity value. +IKA_API struct IkarusEntityValue * ikarus_toggle_value_to_entity_value(IkarusToggleValue * toggle_value); + +IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index a30cf2d..fd17f5b 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -1,16 +1,11 @@ #pragma once -// IMPLEMENTATION_DETAIL_PROPERTY_TYPES - /// \file value.h /// \author Folling #include -#include #include -IKARUS_BEGIN_HEADER - /// \defgroup entity_value Entity Values /// \brief The values stored in entities. /// \details Each entity has a value for each property it is associated with. @@ -19,112 +14,47 @@ IKARUS_BEGIN_HEADER /// \see PropertyType PropertySettings /// @{ -/// \brief A true/false boolean-like value. For example "IsDead". -struct IkarusToggleValue; +IKARUS_BEGIN_HEADER -/// \brief An arbitrary numeric value. For example "Age". -struct IkarusNumberValue; +/// \brief The common type for all values. +struct IkarusValue; -/// \brief An arbitrary textual value. For example "First Name". -struct IkarusTextValue; - -/// \brief The value of an entity associated with a property. -struct IkarusEntityValue; - -/// \brief Creates a toggle value from a boolean. -/// \param value The toggle value. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value); -/// \brief Creates an indeterminate toggle value. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); -/// \brief Sets the value of a toggle value. -/// \param value The toggle value. -/// \pre Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); - -/// \brief Creates a number value from a number. -/// \param value The number value. -/// \pre Must be finite & not NaN. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create(long double value); -/// \brief Creates an indeterminate number value. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); -/// \brief Sets the value of a number value. -/// \param value The number value. -/// \pre Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); - -/// \brief Creates a text value from string. -/// \param value The text value. -/// \pre Must not be null. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create(char const * value); -/// \brief Creates an indeterminate text value. -/// \return The entity value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); -/// \brief Sets the value of a text value. -/// \param value The text value. -/// \pre Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); - -/// \brief Checks if a toggle value is indeterminate. -/// \param value The toggle value. -/// \pre Must not be null. +/// \brief Checks if a value is indeterminate. +/// \param value The value. +/// \pre \li Must not be null. /// \return True if the value is indeterminate, false otherwise. -IKA_API bool ikarus_toggle_value_is_indeterminate(IkarusToggleValue const * value); -/// \brief Checks if a number value is indeterminate. -/// \param value The number value. -/// \pre Must not be null. -/// \return True if the value is indeterminate, false otherwise. -IKA_API bool ikarus_number_value_is_indeterminate(IkarusNumberValue const * value); -/// \brief Checks if a text value is indeterminate. -/// \param value The text value. -/// \pre Must not be null. -/// \return True if the value is indeterminate, false otherwise. -IKA_API bool ikarus_text_value_is_indeterminate(IkarusTextValue const * value); +IKA_API bool ikarus_value_is_indeterminate(IkarusValue const * value); -/// \brief Fetches the underlying value of a toggle value. -/// \param value The toggle value. -/// \return The underlying value. -/// \warning If the value is indeterminate, false is returned. -IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value); - -/// \brief Fetches the underlying value of a number value. -/// \param value The number value. -/// \return The underlying value. -/// \warning If the value is indeterminate, 0.0 is returned. -IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value); - -/// \brief Fetches the underlying value of a text value. -/// \param value The text value. -/// \return A copy of the underlying value. -/// \remark The returned value is a copy and owned by the caller. -/// \warning If the value is indeterminate, an empty string is returned. -IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); +// \brief Converts an entity value to a string. +// \pre \li Must not be null. +// \param value The entity value. +// \return A string representation of the value or null if an error occurred. +// \remark The returned value is a copy and owned by the caller. +IKA_API char const * ikarus_value_to_string(IkarusValue const * value); /// \brief Visits an entity value, calling the appropriate function for the value's type. /// \param value The entity value to visit. -/// \param toggle The function to call if the value is a toggle value. Skipped if null. -/// \param number The function to call if the value is a number value. Skipped if null. -/// \param text The function to call if the value is a text value. Skipped if null. +/// \param toggle_visitor The function to call if the value is a toggle value. Skipped if null. +/// \param number_visitor The function to call if the value is a number value. Skipped if null. +/// \param text_visitor The function to call if the value is a text value. Skipped if null. /// \param data The data passed to the visitor functions. IKA_API void ikarus_value_visit( - IkarusEntityValue * value, - void (*toggle)(IkarusToggleValue *, void *), - void (*number)(IkarusNumberValue *, void *), - void (*text)(IkarusTextValue *, void *), + IkarusValue * value, + void (*toggle_visitor)(struct IkarusToggleValue *, void *), + void (*number_visitor)(struct IkarusNumberValue *, void *), + void (*text_visitor)(struct IkarusTextValue *, void *), + void * data +); + +/// \see ikarus_value_visit +IKA_API void ikarus_value_visit_const( + IkarusValue const * value, + void (*toggle_visitor)(struct IkarusToggleValue const *, void *), + void (*number_visitor)(struct IkarusNumberValue const *, void *), + void (*text_visitor)(struct IkarusTextValue const *, void *), void * data ); IKARUS_END_HEADER + +/// @} diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index 63ce726..df059ac 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -5,10 +5,7 @@ #include #include -#include -#include -#include #include #include #include diff --git a/src/objects/property.cpp b/src/objects/property.cpp new file mode 100644 index 0000000..ba11491 --- /dev/null +++ b/src/objects/property.cpp @@ -0,0 +1,87 @@ +#include "property.hpp" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +IkarusProperty * ikarus_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + struct IkarusPropertyInfo * property_info +) { + LOG_INFO("creating new property"); + + if (project == nullptr) { + LOG_ERROR("project is nullptr"); + return nullptr; + } + + auto ctx = project->function_context(); + + if (property_source == nullptr) { + ctx->set_error("property_source is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return nullptr; + } + + if (name == nullptr) { + ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return nullptr; + } + + if (cppbase::is_empty_or_blank(name)) { + ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return nullptr; + } + + if (property_info == nullptr) { + ctx->set_error("property_info is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return nullptr; + } + + LOG_VERBOSE( + "project={}; name={}; property_source={}; property_info={}", + project->path().c_str(), + name, + std::visit( + cppbase::overload{ + [](IkarusBlueprint * blueprint) { return fmt::format("Blueprint({})", blueprint->id); }, + [](IkarusEntity * entity) { return fmt::format("Entity({})", entity->id); }}, + property_source->data + ), + std::visit( + cppbase::overload{ + [](IkarusTogglePropertyInfo * info) { + return fmt::format( + "Toggle(default_value={})", + cppbase::OwningString{ikarus_value_to_string(ikarus_toggle_value_to_entity_value(info->default_value))} + .data + ); + }, + [](IkarusNumberPropertyInfo * info) { + return fmt::format( + "Number(default_value={})", + cppbase::OwningString{ikarus_value_to_string(ikarus_number_value_to_entity_value(info->default_value))} + .data + ); + }, + [](IkarusTextPropertyInfo * info) { + return fmt::format( + "Text(default_value={})", + cppbase::OwningString{ikarus_value_to_string(ikarus_text_value_to_entity_value(info->default_value))} + .data + ); + }}, + property_info->data + ) + ); +} diff --git a/src/objects/property_info.hpp b/src/objects/property_info.hpp new file mode 100644 index 0000000..8c131f7 --- /dev/null +++ b/src/objects/property_info.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +// this looks a bit cursed, but there's reason to my madness: +// Let's go over the facts: +// 1. The client uses the concrete types (e.g. Ikarus"Toggle"PropertyInfo) +// 2. The API needs to accept a common type (i.e. IkarusPropertyInfo) +// 3. Casting between a concrete subtype and a common type is only defined behaviour if we use inheritance, otherwise an +// allocation is unavoidable +// 4. On the implementation side, we need to be able to distinguish between the concrete types +// +// There's a few ways to model this (using dynamic_casts, using an enum, a union, the visitor pattern, ...) but std::variant +// gives us the most type safety without any performance overhead + +/// \private +struct IkarusPropertyInfo { +public: + using IkarusPropertyInfoData = + std::variant; + +public: + inline explicit IkarusPropertyInfo( + std::variant data + ): + data{data} {} + +public: + [[nodiscard]] inline IkarusPropertyInfoData const& get_data() const { + return data; + } + +private: + IkarusPropertyInfoData data; +}; + +/// \private +struct IkarusTogglePropertyInfo : public IkarusPropertyInfo { +public: + inline IkarusTogglePropertyInfo(): + IkarusPropertyInfo{this} {} + +public: + [[nodiscard]] IkarusToggleValue * get_default_value() const {} + +private: + IkarusToggleValue * default_value{nullptr}; +}; + +/// \private +struct IkarusNumberPropertyInfo : public IkarusPropertyInfo { +public: + inline IkarusNumberPropertyInfo(): + IkarusPropertyInfo{this} {} + +private: + IkarusNumberValue * default_value{nullptr}; +}; + +/// \private +struct IkarusTextPropertyInfo : public IkarusPropertyInfo { +public: + inline IkarusTextPropertyInfo(): + IkarusPropertyInfo{this} {} + +private: + IkarusTextValue * default_value{nullptr}; +}; diff --git a/src/objects/property_source.hpp b/src/objects/property_source.hpp new file mode 100644 index 0000000..16fab4f --- /dev/null +++ b/src/objects/property_source.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include + +/// \private +struct IkarusPropertySource { + std::variant data; +}; diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index 5cbea00..fbb84f2 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -13,6 +13,7 @@ constexpr inline size_t MAXIMUM_ERROR_INFOS = 8; constexpr inline size_t MAXIMUM_ERROR_MESSAGE_LENGTH = 256; +/// \private class FunctionContext { public: explicit FunctionContext(struct IkarusProject * project); @@ -128,7 +129,7 @@ FunctionContext::~FunctionContext() { template requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) -auto FunctionContext::set_error(std::string_view error_message, bool log_error, Infos... infos) { +auto FunctionContext::set_error(std::string_view error_message, bool log_error, Infos... infos) -> void { if (error_message.size() > _project->error_message_buffer.size()) { _project->error_message_buffer.resize(error_message.size() + 1); } diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp new file mode 100644 index 0000000..51020fc --- /dev/null +++ b/src/values/number_value.hpp @@ -0,0 +1,5 @@ +#pragma once + +struct IkarusNumberValue { + long double value; +}; diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp new file mode 100644 index 0000000..20880c4 --- /dev/null +++ b/src/values/text_value.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +/// \private +struct IkarusTextValue { + std::string value; +}; diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp new file mode 100644 index 0000000..a2f2d70 --- /dev/null +++ b/src/values/toggle_value.hpp @@ -0,0 +1,6 @@ +#pragma once + +/// \private +struct IkarusToggleValue { + bool value; +}; From e3d957f3fe3f7663abe946b42f43d48d10f21c0a Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 21 Nov 2023 15:11:42 +0100 Subject: [PATCH 097/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 806c264..538616d 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 806c26457c4e9e3d613e63a8511150390c1b196d +Subproject commit 538616d8ced2d3f04659261ccae3b039d65da004 From da31bbf96ab6e49509afe1d4f2ae0201b61f74e8 Mon Sep 17 00:00:00 2001 From: Folling Date: Fri, 24 Nov 2023 14:52:46 +0100 Subject: [PATCH 098/166] intermediate commit Signed-off-by: Folling --- include/ikarus/objects/object_type.h | 14 +- .../properties/settings/property_settings.h | 1 + src/id.cpp | 8 +- src/id.hpp | 2 + src/objects/blueprint.cpp | 132 ++++++++++++++++-- src/objects/property.cpp | 74 ---------- src/objects/property_info.hpp | 74 ---------- src/objects/property_source.hpp | 2 +- .../migrations/m1_initial_layout.sql | 4 +- src/values/value.cpp | 31 ++++ src/values/value.hpp | 8 ++ 11 files changed, 175 insertions(+), 175 deletions(-) delete mode 100644 src/objects/property_info.hpp create mode 100644 src/values/value.cpp create mode 100644 src/values/value.hpp diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 2539e2d..578919a 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -14,18 +14,12 @@ IKARUS_BEGIN_HEADER enum IkarusObjectType { /// \brief Not an object or no object. IkarusObjectType_None = 0, - /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 0b00000001, - /// \brief An IkarusProperty. - IkarusObjectType_Property = 0b00000010, /// \brief An IkarusEntity. IkarusObjectType_Entity = 0b00000011, - /// \brief An IkarusBlueprintFolder - IkarusObjectType_BlueprintFolder = 0b01000001, - /// \brief An IkarusPropertyFolder - IkarusObjectType_PropertyFolder = 0b01000010, - /// \brief An IkarusEntityFolder - IkarusObjectType_EntityFolder = 0b01000011, + /// \brief An IkarusProperty. + IkarusObjectType_Property = 0b00000010, + /// \brief An IkarusBlueprint. + IkarusObjectType_Blueprint = 0b00000001, }; IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/settings/property_settings.h b/include/ikarus/objects/properties/settings/property_settings.h index 359097c..a47541e 100644 --- a/include/ikarus/objects/properties/settings/property_settings.h +++ b/include/ikarus/objects/properties/settings/property_settings.h @@ -79,6 +79,7 @@ IKA_API void ikarus_property_settings_visit( void * data ); +/// \see ikarus_property_settings_visit IKA_API void ikarus_property_settings_visit_const( struct IkarusPropertySettings const * settings, void (*toggle_property_visitor)(struct IkarusTogglePropertySettings const * settings, void * data), diff --git a/src/id.cpp b/src/id.cpp index cc3913f..7ec6491 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -7,12 +7,12 @@ uint64_t const IKARUS_ID_OBJECT_TYPE_BITS = 8; uint64_t const IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; -auto ikarus_id_get_object_type(IkarusId id) -> IkarusObjectType { - return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); +auto from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { + return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); } -auto ikarus_id_is_equal(IkarusId left, IkarusId right) -> bool { - return left == right; +auto ikarus_id_get_object_type(IkarusId id) -> IkarusObjectType { + return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); } TEST_CASE("id_object_type", "[id]") { diff --git a/src/id.hpp b/src/id.hpp index 4499c17..fc61b05 100644 --- a/src/id.hpp +++ b/src/id.hpp @@ -30,6 +30,8 @@ IKARUS_BEGIN_HEADER /// - last 56 bits: incremented counter generated by the database using IkarusId = int64_t; +IKA_API IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type); + /// \brief Fetches the object type of the given id. /// \param id The id to fetch the object type for. /// \return The object type of the given id. diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index df059ac..d6a38b1 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -1,4 +1,4 @@ -#include "blueprint.hpp" +#include #include #include @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -18,7 +19,7 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - auto ctx = project->function_context(); + auto * ctx = project->function_context(); if (name == nullptr) { ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); @@ -30,7 +31,7 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - LOG_VERBOSE("project={}; name={}", project->path().c_str(), name); + LOG_DEBUG("project={}; name={}", project->path().c_str(), name); VTRYRV( auto id, @@ -45,9 +46,9 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c name )); - auto id = db->last_insert_rowid(); + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - LOG_VERBOSE("id is {}", id); + LOG_DEBUG("blueprint is {}", id); LOG_VERBOSE("inserting blueprint into blueprints table"); @@ -80,7 +81,7 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { auto * ctx = blueprint->project->function_context(); - LOG_VERBOSE("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->id); TRYRV( , @@ -113,13 +114,13 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { auto * ctx = blueprint->project->function_context(); - LOG_VERBOSE("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->id); VTRYRV( auto count, 0, blueprint->project->db() - ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint_id` = ?;", blueprint->id) + ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->id) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch blueprint property count: {}", err), @@ -130,6 +131,10 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { }) ); + LOG_DEBUG("blueprint property count: {}", count); + + LOG_VERBOSE("successfully fetched blueprint property count"); + return static_cast(count); } @@ -146,11 +151,11 @@ void ikarus_blueprint_get_properties( auto * ctx = blueprint->project->function_context(); if (properties_out == nullptr) { - LOG_ERROR("properties_out is nullptr"); + ctx->set_error("properties_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return; } - LOG_VERBOSE("blueprint={}; properties_out_size={}", blueprint->id, properties_out_size); + LOG_DEBUG("blueprint={}; properties_out_size={}", blueprint->id, properties_out_size); IkarusId ids[properties_out_size]; @@ -173,6 +178,8 @@ void ikarus_blueprint_get_properties( }) ); + LOG_DEBUG("blueprint properties: [{}]", fmt::join(ids, ids + properties_out_size, ", ")); + for (size_t i = 0; i < properties_out_size; ++i) { /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) properties_out[i] = blueprint->project->get_property(ids[i]); @@ -180,3 +187,108 @@ void ikarus_blueprint_get_properties( LOG_VERBOSE("successfully fetched blueprint properties"); } + +size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { + LOG_VERBOSE("fetching blueprint linked entity count"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return 0; + } + + auto * ctx = blueprint->project->function_context(); + + LOG_DEBUG("blueprint={}", blueprint->id); + + VTRYRV( + auto count, + 0, + blueprint->project->db() + ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->id) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint linked entity count: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_DEBUG("blueprint linked entity count: {}", count); + + LOG_VERBOSE("successfully fetched blueprint linked entity count: {}", count); + + return static_cast(count); +} + +void ikarus_blueprint_get_linked_entities( + IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size +) { + LOG_VERBOSE("fetching blueprint linked entities"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return; + } + + auto * ctx = blueprint->project->function_context(); + + if (entities_out == nullptr) { + ctx->set_error("entities_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return; + } + + LOG_DEBUG("blueprint={}; entities_out_size={}", blueprint->id, entities_out_size); + + IkarusId ids[entities_out_size]; + + TRYRV( + , + blueprint->project->db() + ->query_many_buffered( + "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", + static_cast(ids), + entities_out_size, + blueprint->id + ) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint linked entity ids: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_DEBUG("blueprint linked entities: [{}]", fmt::join(ids, ids + entities_out_size, ", ")); + + for (size_t i = 0; i < entities_out_size; ++i) { + /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + entities_out[i] = blueprint->project->get_entity(ids[i]); + } + + LOG_VERBOSE("successfully fetched blueprint linked entities"); +} + +IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint) { + return const_cast(ikarus_blueprint_to_object_const(blueprint)); +} + +IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint) { + LOG_VERBOSE("casting blueprint to object"); + + if (blueprint == nullptr) { + LOG_ERROR("blueprint is nullptr"); + return nullptr; + } + + // auto * ctx = blueprint->project->function_context(); + + LOG_DEBUG("blueprint={}", blueprint->id); + + LOG_VERBOSE("successfully casted blueprint to object"); + + return static_cast(blueprint); +} diff --git a/src/objects/property.cpp b/src/objects/property.cpp index ba11491..80d9031 100644 --- a/src/objects/property.cpp +++ b/src/objects/property.cpp @@ -9,79 +9,5 @@ #include #include #include -#include #include #include - -IkarusProperty * ikarus_property_create( - struct IkarusProject * project, - char const * name, - struct IkarusPropertySource * property_source, - struct IkarusPropertyInfo * property_info -) { - LOG_INFO("creating new property"); - - if (project == nullptr) { - LOG_ERROR("project is nullptr"); - return nullptr; - } - - auto ctx = project->function_context(); - - if (property_source == nullptr) { - ctx->set_error("property_source is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - if (name == nullptr) { - ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - if (cppbase::is_empty_or_blank(name)) { - ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - if (property_info == nullptr) { - ctx->set_error("property_info is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - LOG_VERBOSE( - "project={}; name={}; property_source={}; property_info={}", - project->path().c_str(), - name, - std::visit( - cppbase::overload{ - [](IkarusBlueprint * blueprint) { return fmt::format("Blueprint({})", blueprint->id); }, - [](IkarusEntity * entity) { return fmt::format("Entity({})", entity->id); }}, - property_source->data - ), - std::visit( - cppbase::overload{ - [](IkarusTogglePropertyInfo * info) { - return fmt::format( - "Toggle(default_value={})", - cppbase::OwningString{ikarus_value_to_string(ikarus_toggle_value_to_entity_value(info->default_value))} - .data - ); - }, - [](IkarusNumberPropertyInfo * info) { - return fmt::format( - "Number(default_value={})", - cppbase::OwningString{ikarus_value_to_string(ikarus_number_value_to_entity_value(info->default_value))} - .data - ); - }, - [](IkarusTextPropertyInfo * info) { - return fmt::format( - "Text(default_value={})", - cppbase::OwningString{ikarus_value_to_string(ikarus_text_value_to_entity_value(info->default_value))} - .data - ); - }}, - property_info->data - ) - ); -} diff --git a/src/objects/property_info.hpp b/src/objects/property_info.hpp deleted file mode 100644 index 8c131f7..0000000 --- a/src/objects/property_info.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include -#include - -// this looks a bit cursed, but there's reason to my madness: -// Let's go over the facts: -// 1. The client uses the concrete types (e.g. Ikarus"Toggle"PropertyInfo) -// 2. The API needs to accept a common type (i.e. IkarusPropertyInfo) -// 3. Casting between a concrete subtype and a common type is only defined behaviour if we use inheritance, otherwise an -// allocation is unavoidable -// 4. On the implementation side, we need to be able to distinguish between the concrete types -// -// There's a few ways to model this (using dynamic_casts, using an enum, a union, the visitor pattern, ...) but std::variant -// gives us the most type safety without any performance overhead - -/// \private -struct IkarusPropertyInfo { -public: - using IkarusPropertyInfoData = - std::variant; - -public: - inline explicit IkarusPropertyInfo( - std::variant data - ): - data{data} {} - -public: - [[nodiscard]] inline IkarusPropertyInfoData const& get_data() const { - return data; - } - -private: - IkarusPropertyInfoData data; -}; - -/// \private -struct IkarusTogglePropertyInfo : public IkarusPropertyInfo { -public: - inline IkarusTogglePropertyInfo(): - IkarusPropertyInfo{this} {} - -public: - [[nodiscard]] IkarusToggleValue * get_default_value() const {} - -private: - IkarusToggleValue * default_value{nullptr}; -}; - -/// \private -struct IkarusNumberPropertyInfo : public IkarusPropertyInfo { -public: - inline IkarusNumberPropertyInfo(): - IkarusPropertyInfo{this} {} - -private: - IkarusNumberValue * default_value{nullptr}; -}; - -/// \private -struct IkarusTextPropertyInfo : public IkarusPropertyInfo { -public: - inline IkarusTextPropertyInfo(): - IkarusPropertyInfo{this} {} - -private: - IkarusTextValue * default_value{nullptr}; -}; diff --git a/src/objects/property_source.hpp b/src/objects/property_source.hpp index 16fab4f..e2a2739 100644 --- a/src/objects/property_source.hpp +++ b/src/objects/property_source.hpp @@ -2,7 +2,7 @@ #include -#include +#include /// \private struct IkarusPropertySource { diff --git a/src/persistence/migrations/m1_initial_layout.sql b/src/persistence/migrations/m1_initial_layout.sql index 7b49cb6..d94fa10 100644 --- a/src/persistence/migrations/m1_initial_layout.sql +++ b/src/persistence/migrations/m1_initial_layout.sql @@ -39,7 +39,7 @@ CREATE TABLE `entities` FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; -CREATE TABLE `entity_blueprints` +CREATE TABLE `entity_blueprint_links` ( `entity` INT NOT NULL, `blueprint` INT NOT NULL, @@ -50,7 +50,7 @@ CREATE TABLE `entity_blueprints` FOREIGN KEY (`blueprint`) REFERENCES `blueprints` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; -CREATE INDEX `entity_blueprints_blueprint` ON `entity_blueprints` (`blueprint`); +CREATE INDEX `entity_blueprints_blueprint` ON `entity_blueprint_links` (`blueprint`); CREATE TABLE `properties` ( diff --git a/src/values/value.cpp b/src/values/value.cpp new file mode 100644 index 0000000..52b4d18 --- /dev/null +++ b/src/values/value.cpp @@ -0,0 +1,31 @@ +#include "ikarus/values/value.h" + +#include + +#include + +#include +#include +#include +#include + +bool ikarus_value_is_indeterminate(IkarusValue const * value) { + return value->indeterminate; +} + +char const * ikarus_value_to_string(IkarusValue const * value) { + auto str = std::visit( + cppbase::overloaded{ + [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->value ? "true" : "false"; }, + [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->value); }, + [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->value); }, + }, + value->data + ); + + char * ret = new char[str.size() + 1]; + + std::strncpy(ret, str.data(), str.size() + 1); + + return ret; +} diff --git a/src/values/value.hpp b/src/values/value.hpp new file mode 100644 index 0000000..1d53154 --- /dev/null +++ b/src/values/value.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct IkarusValue { + bool indeterminate; + std::variant data; +}; From f847d30c064a146eca0a4752e5549be478446dbf Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 26 Nov 2023 13:55:14 +0100 Subject: [PATCH 099/166] implement toggle/number/text values Signed-off-by: Folling --- include/ikarus/errors.h | 16 +++++----- include/ikarus/values/number_value.h | 14 ++++----- include/ikarus/values/text_value.h | 15 ++++----- include/ikarus/values/toggle_value.h | 14 ++++----- include/ikarus/values/value.h | 18 +++++++---- src/values/number_value.cpp | 26 ++++++++++++++++ src/values/number_value.hpp | 30 ++++++++++++++++-- src/values/text_value.cpp | 26 ++++++++++++++++ src/values/text_value.hpp | 29 ++++++++++++++++-- src/values/toggle_value.cpp | 26 ++++++++++++++++ src/values/toggle_value.hpp | 29 ++++++++++++++++-- src/values/value.cpp | 46 +++++++++++++++++++++++++--- src/values/value.hpp | 37 ++++++++++++++++++++-- vendor/sqlitecpp | 2 +- 14 files changed, 280 insertions(+), 48 deletions(-) create mode 100644 src/values/number_value.cpp create mode 100644 src/values/text_value.cpp create mode 100644 src/values/toggle_value.cpp diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index f501c22..ce2f31d 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -33,28 +33,28 @@ enum IkarusErrorInfo { /// \brief No error occurred. IkarusErrorInfo_Type_None = 0x0002000000000000, /// \brief The user misused the API. - /// \example Accessing a resource that does not exist. + /// Example: Accessing a resource that does not exist. IkarusErrorInfo_Type_Client_Misuse = 0x0002000100000001, /// \brief The user provided invalid input. - /// \example Passing null for a pointer that must not be null. + /// Example: Passing null for a pointer that must not be null. IkarusErrorInfo_Type_Client_Input = 0x0002000100000002, /// \brief An error occurred while interacting with a dependency from ikarus. - /// \example An error occurred in the underlying OS library. + /// Example: An error occurred in the underlying OS library. IkarusErrorInfo_Type_SubSystem_Dependency = 0x0002000200000001, /// \brief An error occurred while interacting with the database. - /// \example An error occurred while executing a query. + /// Example: An error occurred while executing a query. IkarusErrorInfo_Type_SubSystem_Database = 0x0002000200000002, /// \brief An error occurred while interacting with the filesystem. - /// \example An error occurred while reading a file. + /// Example: An error occurred while reading a file. IkarusErrorInfo_Type_SubSystem_Filesystem = 0x0002000200000003, /// \brief A datapoint within ikarus is invalid for the current state of the system. - /// \example The name of an object is found to be invalid UTF8. + /// Example: The name of an object is found to be invalid UTF8. IkarusErrorInfo_Type_LibIkarus_InvalidState = 0x0002000300000001, /// \brief LibIkarus is unable to perform a certain operation that should succeed. - /// \example Migrating a project fails + /// Example: Migrating a project fails IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation = 0x0002000300000002, /// \brief LibIkarus is unable to perform a certain operation within a given timeframe. - /// \example A query takes longer than the timeout. + /// Example: A query takes longer than the timeout. IkarusErrorInfo_Type_LibIkarus_Timeout = 0x0002000300000003, /// \brief The type of error is unknown. IkarusErrorInfo_Type_Unknown = 0xFFFFFFFF, diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index 2c054f8..a489cd6 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -24,21 +24,21 @@ IKA_API IkarusNumberValue * ikarus_number_value_create(long double value); /// \remark Must be freed with #ikarus_free. IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); +/// \brief Fetches the underlying value of a number value. +/// \param value The number value. +/// \return The underlying value. +/// \warning Undefined if the value is indeterminate. +IKA_API long double ikarus_number_value_get(IkarusNumberValue const * value); + /// \brief Sets the value of a number value. /// \param value The number value. /// \pre \li Must not be null. /// \param new_value The new value. IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); -/// \brief Fetches the underlying value of a number value. -/// \param value The number value. -/// \return The underlying value. -/// \warning If the value is indeterminate, false is returned. -IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value); - /// \brief Converts a number value to an entity value. /// \param number_value The number value to convert. /// \return The converted entity value. -IKA_API struct IkarusEntityValue * ikarus_number_value_to_entity_value(IkarusNumberValue * number_value); +IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * number_value); IKARUS_END_HEADER diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index 867a638..b9ea9c9 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -24,21 +24,22 @@ IKA_API IkarusTextValue * ikarus_text_value_create(char const * value); /// \remark Must be freed with #ikarus_free. IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); +/// \brief Fetches the underlying value of a text value. +/// \param value The text value. +/// \return The underlying value. +/// \warning Undefined if the value is indeterminate. +/// \remark The value is owned by libikarus and must not be freed. +IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); + /// \brief Sets the value of a text value. /// \param value The text value. /// \pre \li Must not be null. /// \param new_value The new value. IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); -/// \brief Fetches the underlying value of a text value. -/// \param value The text value. -/// \return The underlying value. -/// \warning If the value is indeterminate, false is returned. -IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); - /// \brief Converts a text value to an entity value. /// \param text_value The text value to convert. /// \return The converted entity value. -IKA_API struct IkarusEntityValue * ikarus_text_value_to_entity_value(IkarusTextValue * text_value); +IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * text_value); IKARUS_END_HEADER diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index 7c77870..e653482 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -24,21 +24,21 @@ IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value); /// \remark Must be freed with #ikarus_free. IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); +/// \brief Fetches the underlying value of a toggle value. +/// \param value The toggle value. +/// \return The underlying value. +/// \warning Undefined if the value is indeterminate. +IKA_API bool ikarus_toggle_value_get(IkarusToggleValue const * value); + /// \brief Sets the value of a toggle value. /// \param value The toggle value. /// \pre \li Must not be null. /// \param new_value The new value. IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); -/// \brief Fetches the underlying value of a toggle value. -/// \param value The toggle value. -/// \return The underlying value. -/// \warning If the value is indeterminate, false is returned. -IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value); - /// \brief Converts a toggle value to an entity value. /// \param toggle_value The toggle value to convert. /// \return The converted entity value. -IKA_API struct IkarusEntityValue * ikarus_toggle_value_to_entity_value(IkarusToggleValue * toggle_value); +IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * toggle_value); IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index fd17f5b..b8b95fe 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -4,7 +4,6 @@ /// \author Folling #include -#include /// \defgroup entity_value Entity Values /// \brief The values stored in entities. @@ -25,15 +24,22 @@ struct IkarusValue; /// \return True if the value is indeterminate, false otherwise. IKA_API bool ikarus_value_is_indeterminate(IkarusValue const * value); -// \brief Converts an entity value to a string. -// \pre \li Must not be null. -// \param value The entity value. -// \return A string representation of the value or null if an error occurred. -// \remark The returned value is a copy and owned by the caller. +/// \brief Sets the indeterminate state of a value. +/// \param value The value. +/// \pre \li Must not be null. +/// \param indeterminate The new indeterminate state. +IKA_API void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate); + +/// \brief Converts an entity value to a string. +/// \pre \li Must not be null. +/// \param value The entity value. +/// \return A string representation of the value or null if an error occurred. +/// \remark The returned value is owned by the caller. IKA_API char const * ikarus_value_to_string(IkarusValue const * value); /// \brief Visits an entity value, calling the appropriate function for the value's type. /// \param value The entity value to visit. +/// \pre \li Must not be null. /// \param toggle_visitor The function to call if the value is a toggle value. Skipped if null. /// \param number_visitor The function to call if the value is a number value. Skipped if null. /// \param text_visitor The function to call if the value is a text value. Skipped if null. diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp new file mode 100644 index 0000000..b4d3413 --- /dev/null +++ b/src/values/number_value.cpp @@ -0,0 +1,26 @@ +#include "ikarus/values/number_value.h" + +#include + +IkarusNumberValue * ikarus_number_value_create(long double value) { + return new IkarusNumberValue{value}; +} + +IkarusNumberValue * ikarus_number_value_create_indeterminate() { + auto * ret = new IkarusNumberValue{0.0}; + ret->set_intermediate(true); + + return ret; +} + +long double ikarus_number_value_get(IkarusNumberValue const * value) { + return value->get_value(); +} + +void ikarus_number_value_set(IkarusNumberValue * value, long double new_value) { + value->set_value(new_value); +} + +struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * number_value) { + return static_cast(number_value); +} diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index 51020fc..e495be5 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -1,5 +1,31 @@ #pragma once -struct IkarusNumberValue { - long double value; +#include + +/// \private +struct IkarusNumberValue final : IkarusValue { +public: + explicit IkarusNumberValue(long double value): + IkarusValue{this}, + _value{value} {} + + IkarusNumberValue(IkarusNumberValue const&) = default; + IkarusNumberValue(IkarusNumberValue&&) = default; + + IkarusNumberValue& operator=(IkarusNumberValue const&) = default; + IkarusNumberValue& operator=(IkarusNumberValue&&) = default; + + ~IkarusNumberValue() override = default; + +public: + [[nodiscard]] long double get_value() const { + return _value; + } + + void set_value(long double value) { + _value = value; + } + +private: + long double _value; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp new file mode 100644 index 0000000..6eb839c --- /dev/null +++ b/src/values/text_value.cpp @@ -0,0 +1,26 @@ +#include "ikarus/values/text_value.h" + +#include "text_value.hpp" + +IkarusTextValue * ikarus_text_value_create(char const * value) { + return new IkarusTextValue{value}; +} + +IkarusTextValue * ikarus_text_value_create_indeterminate() { + auto * ret = new IkarusTextValue{""}; + ret->set_intermediate(true); + + return ret; +} + +char const * ikarus_text_value_get(IkarusTextValue const * value) { + return value->get_value().data(); +} + +void ikarus_text_value_set(IkarusTextValue * value, char const * new_value) { + value->set_value(new_value); +} + +struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * text_value) { + return static_cast(text_value); +} diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 20880c4..3766513 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -2,7 +2,32 @@ #include +#include + /// \private -struct IkarusTextValue { - std::string value; +struct IkarusTextValue final : IkarusValue { +public: + explicit IkarusTextValue(std::string value): + IkarusValue{this}, + _value(std::move(value)) {} + + IkarusTextValue(IkarusTextValue const&) = default; + IkarusTextValue(IkarusTextValue&&) noexcept = default; + + IkarusTextValue& operator=(IkarusTextValue const&) = default; + IkarusTextValue& operator=(IkarusTextValue&&) noexcept = default; + + ~IkarusTextValue() override = default; + +public: + [[nodiscard]] std::string_view get_value() const noexcept { + return _value; + } + + void set_value(std::string_view value) { + _value = value; + } + +private: + std::string _value; }; diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp new file mode 100644 index 0000000..30e64c8 --- /dev/null +++ b/src/values/toggle_value.cpp @@ -0,0 +1,26 @@ +#include "ikarus/values/toggle_value.h" + +#include "toggle_value.hpp" + +IkarusToggleValue * ikarus_toggle_value_create(bool value) { + return new IkarusToggleValue{value}; +} + +IkarusToggleValue * ikarus_toggle_value_create_indeterminate() { + auto * ret = new IkarusToggleValue{false}; + ret->set_intermediate(true); + + return ret; +} + +bool ikarus_toggle_value_get(IkarusToggleValue const * value) { + return value->get_value(); +} + +void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value) { + value->set_value(new_value); +} + +struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * toggle_value) { + return static_cast(toggle_value); +} diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index a2f2d70..b526476 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -1,6 +1,31 @@ #pragma once +#include + /// \private -struct IkarusToggleValue { - bool value; +struct IkarusToggleValue final : IkarusValue { +public: + explicit IkarusToggleValue(bool value): + IkarusValue{this}, + _value{value} {} + + IkarusToggleValue(IkarusToggleValue const&) = default; + IkarusToggleValue(IkarusToggleValue&&) = default; + + IkarusToggleValue& operator=(IkarusToggleValue const&) = default; + IkarusToggleValue& operator=(IkarusToggleValue&&) = default; + + ~IkarusToggleValue() override = default; + +public: + [[nodiscard]] bool get_value() const { + return _value; + } + + void set_value(bool value) { + _value = value; + } + +private: + bool _value; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index 52b4d18..f61f2bb 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -10,22 +10,60 @@ #include bool ikarus_value_is_indeterminate(IkarusValue const * value) { - return value->indeterminate; + return value->is_interminate(); +} + +void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { + value->set_intermediate(indeterminate); } char const * ikarus_value_to_string(IkarusValue const * value) { - auto str = std::visit( + auto const str = std::visit( cppbase::overloaded{ [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->value ? "true" : "false"; }, [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->value); }, [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->value); }, }, - value->data + value->get_data() ); - char * ret = new char[str.size() + 1]; + auto * const ret = new char[str.size() + 1]; std::strncpy(ret, str.data(), str.size() + 1); return ret; } + +void ikarus_value_visit( + IkarusValue * value, + void (*toggle_visitor)(IkarusToggleValue *, void *), + void (*number_visitor)(IkarusNumberValue *, void *), + void (*text_visitor)(IkarusTextValue *, void *), + void * data +) { + std::visit( + cppbase::overloaded{ + [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); }, + }, + value->get_data() + ); +} + +void ikarus_value_visit_const( + IkarusValue const * value, + void (*toggle_visitor)(IkarusToggleValue const *, void *), + void (*number_visitor)(IkarusNumberValue const *, void *), + void (*text_visitor)(IkarusTextValue const *, void *), + void * data +) { + std::visit( + cppbase::overloaded{ + [toggle_visitor, data](IkarusToggleValue const * toggle_value) { toggle_visitor(toggle_value, data); }, + [number_visitor, data](IkarusNumberValue const * number_value) { number_visitor(number_value, data); }, + [text_visitor, data](IkarusTextValue const * text_value) { text_visitor(text_value, data); }, + }, + value->get_data() + ); +} diff --git a/src/values/value.hpp b/src/values/value.hpp index 1d53154..0adc61c 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -3,6 +3,39 @@ #include struct IkarusValue { - bool indeterminate; - std::variant data; +public: + using Data = std::variant::variant; + +public: + explicit IkarusValue(Data data): + _data(data) {} + + IkarusValue(IkarusValue const&) = default; + IkarusValue(IkarusValue&&) noexcept = default; + + IkarusValue& operator=(IkarusValue const&) = default; + IkarusValue& operator=(IkarusValue&&) noexcept = default; + + virtual ~IkarusValue(); + +public: + [[nodiscard]] inline bool is_interminate() const { + return _indeterminate; + } + + void set_intermediate(bool value) { + _indeterminate = value; + } + + [[nodiscard]] inline Data& get_data() { + return _data; + } + + [[nodiscard]] inline Data const& get_data() const { + return _data; + } + +private: + bool _indeterminate{false}; + Data _data; }; diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 538616d..3057656 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 538616d8ced2d3f04659261ccae3b039d65da004 +Subproject commit 3057656ff277294ab424af90e553e630c2a5e8f7 From c98afbdfa684bb5568ad9ca01d9af92270c15af0 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 27 Nov 2023 11:24:55 +0100 Subject: [PATCH 100/166] update sqlitecpp & merge property settings into properties Signed-off-by: Folling --- include/ikarus/errors.h | 2 +- include/ikarus/global.h | 2 +- src/id.hpp => include/ikarus/id.h | 0 include/ikarus/objects/object.h | 21 ++-- .../objects/properties/number_property.h | 25 +++- include/ikarus/objects/properties/property.h | 20 ++- .../ikarus/objects/properties/property_type.h | 6 - .../ikarus/objects/properties/text_property.h | 25 +++- .../objects/properties/toggle_property.h | 25 +++- include/ikarus/values/number_value.h | 4 +- include/ikarus/values/text_value.h | 4 +- include/ikarus/values/toggle_value.h | 4 +- include/ikarus/values/value.h | 2 +- src/errors.cpp | 30 ++--- src/id.cpp | 24 +--- src/objects/blueprint.cpp | 66 +++++----- src/objects/blueprint.hpp | 14 ++- src/objects/entity.cpp | 0 src/objects/entity.hpp | 11 +- src/objects/object.cpp | 13 ++ src/objects/object.hpp | 29 +++-- src/objects/properties/number_property.cpp | 0 src/objects/properties/number_property.hpp | 5 + src/objects/properties/property.cpp | 0 src/objects/properties/property.hpp | 15 +++ src/objects/properties/property_source.cpp | 0 src/objects/properties/property_source.hpp | 30 +++++ src/objects/properties/text_property.cpp | 0 src/objects/properties/text_property.hpp | 8 ++ src/objects/properties/toggle_property.cpp | 0 src/objects/properties/toggle_property.hpp | 7 ++ src/objects/property.cpp | 13 -- src/objects/property.hpp | 9 -- src/objects/property_source.hpp | 10 -- src/persistence/function_context.cpp | 18 +++ src/persistence/function_context.hpp | 53 ++++++++ src/persistence/project.cpp | 49 ++++++++ src/persistence/project.hpp | 119 ++++-------------- vendor/sqlitecpp | 2 +- 39 files changed, 412 insertions(+), 253 deletions(-) rename src/id.hpp => include/ikarus/id.h (100%) create mode 100644 src/objects/entity.cpp create mode 100644 src/objects/object.cpp create mode 100644 src/objects/properties/number_property.cpp create mode 100644 src/objects/properties/number_property.hpp create mode 100644 src/objects/properties/property.cpp create mode 100644 src/objects/properties/property.hpp create mode 100644 src/objects/properties/property_source.cpp create mode 100644 src/objects/properties/property_source.hpp create mode 100644 src/objects/properties/text_property.cpp create mode 100644 src/objects/properties/text_property.hpp create mode 100644 src/objects/properties/toggle_property.cpp create mode 100644 src/objects/properties/toggle_property.hpp delete mode 100644 src/objects/property.cpp delete mode 100644 src/objects/property.hpp delete mode 100644 src/objects/property_source.hpp create mode 100644 src/persistence/function_context.cpp create mode 100644 src/persistence/function_context.hpp create mode 100644 src/persistence/project.cpp diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index ce2f31d..9dc876d 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -57,7 +57,7 @@ enum IkarusErrorInfo { /// Example: A query takes longer than the timeout. IkarusErrorInfo_Type_LibIkarus_Timeout = 0x0002000300000003, /// \brief The type of error is unknown. - IkarusErrorInfo_Type_Unknown = 0xFFFFFFFF, + IkarusErrorInfo_Type_Unknown = 0xFFFFFFFFFFFFFFFF, }; /// \brief Gets the name of an error info. diff --git a/include/ikarus/global.h b/include/ikarus/global.h index 1572b24..bf9e5d5 100644 --- a/include/ikarus/global.h +++ b/include/ikarus/global.h @@ -5,7 +5,7 @@ #include -/// \addtogroup global Global +/// \defgroup global Global /// \brief Information relevant to the entire library. /// @{ diff --git a/src/id.hpp b/include/ikarus/id.h similarity index 100% rename from src/id.hpp rename to include/ikarus/id.h diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index a2cce6a..abaa580 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -8,12 +8,9 @@ /// \defgroup object Objects /// \brief Objects are a compound type of all types of objects in the database. /// \details The following objects currently exist: -/// - blueprints -/// - properties -/// - entities -/// - blueprint folders -/// - property folders -/// - entity folders +/// - \ref blueprint.h "Blueprints" +/// - \ref property.h "Properties" +/// - \ref entity.h "Entities" /// @{ IKARUS_BEGIN_HEADER @@ -21,6 +18,16 @@ IKARUS_BEGIN_HEADER /// \brief A generic object. Wraps all types of objects, including folders. struct IkarusObject; +/// \brief Compares two objects for equality. +/// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the same +/// object. +/// \param lhs The left hand side object. +/// \pre \li Must not be null. +/// \param rhs The right hand side object. +/// \pre \li Must not be null. +/// \return True if the objects are equal, false otherwise. +IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs); + /// \brief Visits an object. Calling the appropriate function for the object's type. /// \param object The object to visit. /// \param blueprint_visitor The function to call if the object is a blueprint. Skipped if null. @@ -55,4 +62,4 @@ IKA_API void ikarus_object_visit_const( IKARUS_END_HEADER -// @} +/// @} diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 9045140..7787cc6 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -14,10 +14,27 @@ IKARUS_BEGIN_HEADER struct IkarusNumberProperty; IKA_API IkarusNumberProperty * ikarus_number_property_create( - struct IkarusProject * project, - char const * name, - struct IkarusPropertySource * property_source, - struct IkarusNumberSettings * settings + struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source +); + +/// \brief Sets the default value for a number property. +/// \param property The number property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(struct IkarusNumberProperty * property); + +/// \brief Sets the default value for a number property. +/// \param property The number property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values +/// and other settings. +IKA_API void ikarus_number_property_set_default_value( + struct IkarusNumberProperty * property, struct IkarusNumberValue * default_value ); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index f1ebf49..1da35e6 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -29,8 +29,12 @@ IKARUS_BEGIN_HEADER /// /// Every property has settings which can be used to customise the property further. /// Two settings that are shared among all properties are the following: -/// - Multiple -/// - Allow undefined +/// - List +/// - May be undefined +/// +/// Additionally, each property has a default value. If no default value is provided, a sensible default is chosen. +/// Setting a default value that isn't valid for the property is an error. Changing settings so that the current default value +/// becomes invalid is valid but unsets the custom default value. /// /// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. /// The latter allows you to specify an "unknown" value for a property. @@ -43,6 +47,7 @@ IKARUS_BEGIN_HEADER /// /// We call properties within entities "Entity Properties" and properties within blueprints "Blueprint Properties". /// +/// /// \remark Properties are scoped to the blueprint or entity they are associated with. /// \remark Values for properties are lazily created as space saving measure. /// Fetching the value for some property of some entity will return the property's default value if none is specified. @@ -65,17 +70,6 @@ IKA_API void ikarus_property_delete(IkarusProperty * property); /// \remark Must be freed using #ikarus_free. IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property); -/// \brief Gets the settings of a property. -/// \param property The property to get the settings of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The settings of the property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySettings * ikarus_property_get_settings(IkarusProperty * property); - -/// \see ikarus_property_get_settings -IKA_API struct IkarusPropertySettings const * ikarus_property_get_settings_const(IkarusProperty const * property); - /// \brief Gets the source of a property. /// \param property The property to get the source of. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/properties/property_type.h b/include/ikarus/objects/properties/property_type.h index 89e5fa1..ee6ec19 100644 --- a/include/ikarus/objects/properties/property_type.h +++ b/include/ikarus/objects/properties/property_type.h @@ -22,12 +22,6 @@ enum IkarusPropertyType { IkarusPropertyType_Text, }; -/// \brief Fetches the default value for a property type. -/// \remark Not to be confused with the default value of a property. See ikarus_property_get_default_value -/// \param type The property type. -/// \return The default value for the property type or null if an error occurs. -IKA_API struct IkarusValue * ikarus_property_type_get_default_default_value(IkarusPropertyType type); - IKARUS_END_HEADER /// @} diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index f7975b3..ad90bf6 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -14,10 +14,27 @@ IKARUS_BEGIN_HEADER struct IkarusTextProperty; IKA_API IkarusTextProperty * ikarus_text_property_create( - struct IkarusProject * project, - char const * name, - struct IkarusPropertySource * property_source, - struct IkarusTextSettings * settings + struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source +); + +/// \brief Sets the default value for a text property. +/// \param property The text property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property); + +/// \brief Sets the default value for a text property. +/// \param property The text property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values +/// and other settings. +IKA_API void ikarus_text_property_set_default_value( + struct IkarusTextProperty * property, struct IkarusTextValue * default_value ); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index baa0801..9e8119d 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -14,10 +14,27 @@ IKARUS_BEGIN_HEADER struct IkarusToggleProperty; IKA_API IkarusToggleProperty * ikarus_toggle_property_create( - struct IkarusProject * project, - char const * name, - struct IkarusPropertySource * property_source, - struct IkarusToggleSettings * settings + struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source +); + +/// \brief Sets the default value for a toggle property. +/// \param property The toggle property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark The settings take ownership of the value, the caller must not free it. +IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property); + +/// \brief Sets the default value for a toggle property. +/// \param property The toggle property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param default_value The default value. +/// \pre \li Must not be null. +/// \pre \li Must be a valid value for the property. +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values +/// and other settings. +IKA_API void ikarus_toggle_property_set_default_value( + struct IkarusToggleProperty * property, struct IkarusToggleValue * default_value ); IKARUS_END_HEADER diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index a489cd6..d4b6446 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -5,7 +5,7 @@ #include -/// \addtogroup entity_value Entity Values +/// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER @@ -42,3 +42,5 @@ IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * number_value); IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index b9ea9c9..8562ab5 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -5,7 +5,7 @@ #include -/// \addtogroup entity_value Entity Values +/// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER @@ -43,3 +43,5 @@ IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * text_value); IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index e653482..fed97d2 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -5,7 +5,7 @@ #include -/// \addtogroup entity_value Entity Values +/// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER @@ -42,3 +42,5 @@ IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * toggle_value); IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index b8b95fe..1cf1fd0 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -5,7 +5,7 @@ #include -/// \defgroup entity_value Entity Values +/// \defgroup values Values /// \brief The values stored in entities. /// \details Each entity has a value for each property it is associated with. /// The value is of the type specified by the property and constrained by the property's settings. diff --git a/src/errors.cpp b/src/errors.cpp index 19c3bf9..54681f2 100644 --- a/src/errors.cpp +++ b/src/errors.cpp @@ -2,21 +2,21 @@ char const * get_error_info_name(IkarusErrorInfo info) { switch (info) { - case IkarusErrorInfo_Source_None: return "IkarusErrorSource_None"; - case IkarusErrorInfo_Source_Client: return "IkarusErrorSource_Client"; - case IkarusErrorInfo_Source_SubSystem: return "IkarusErrorSource_SubSystem"; - case IkarusErrorInfo_Source_LibIkarus: return "IkarusErrorSource_LibIkarus"; - case IkarusErrorInfo_Source_Unknown: return "IkarusErrorSource_Unknown"; - case IkarusErrorInfo_Type_None: return "IkarusErrorType_None"; - case IkarusErrorInfo_Type_Client_Misuse: return "IkarusErrorType_Client_Misuse"; - case IkarusErrorInfo_Type_Client_Input: return "IkarusErrorType_Client_Input"; - case IkarusErrorInfo_Type_SubSystem_Dependency: return "IkarusErrorType_SubSystem_Dependency"; - case IkarusErrorInfo_Type_SubSystem_Database: return "IkarusErrorType_SubSystem_Database"; - case IkarusErrorInfo_Type_SubSystem_Filesystem: return "IkarusErrorType_SubSystem_Filesystem"; - case IkarusErrorInfo_Type_LibIkarus_InvalidState: return "IkarusErrorType_LibIkarus_InvalidState"; - case IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation: return "IkarusErrorType_LibIkarus_CannotPerformOperation"; - case IkarusErrorInfo_Type_LibIkarus_Timeout: return "IkarusErrorType_LibIkarus_Timeout"; - case IkarusErrorInfo_Type_Unknown: return "IkarusErrorType_Unknown"; + case IkarusErrorInfo_Source_None: return "IkarusErrorInfo_Source_None"; + case IkarusErrorInfo_Source_Client: return "IkarusErrorInfo_Source_Client"; + case IkarusErrorInfo_Source_SubSystem: return "IkarusErrorInfo_Source_SubSystem"; + case IkarusErrorInfo_Source_LibIkarus: return "IkarusErrorInfo_Source_LibIkarus"; + case IkarusErrorInfo_Source_Unknown: return "IkarusErrorInfo_Source_Unknown"; + case IkarusErrorInfo_Type_None: return "IkarusErrorInfo_Type_None"; + case IkarusErrorInfo_Type_Client_Misuse: return "IkarusErrorInfo_Type_Client_Misuse"; + case IkarusErrorInfo_Type_Client_Input: return "IkarusErrorInfo_Type_Client_Input"; + case IkarusErrorInfo_Type_SubSystem_Dependency: return "IkarusErrorInfo_Type_SubSystem_Dependency"; + case IkarusErrorInfo_Type_SubSystem_Database: return "IkarusErrorInfo_Type_SubSystem_Database"; + case IkarusErrorInfo_Type_SubSystem_Filesystem: return "IkarusErrorInfo_Type_SubSystem_Filesystem"; + case IkarusErrorInfo_Type_LibIkarus_InvalidState: return "IkarusErrorInfo_Type_LibIkarus_InvalidState"; + case IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation: return "IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation"; + case IkarusErrorInfo_Type_LibIkarus_Timeout: return "IkarusErrorInfo_Type_LibIkarus_Timeout"; + case IkarusErrorInfo_Type_Unknown: return "IkarusErrorInfo_Type_Unknown"; default: return "Unknown"; } } diff --git a/src/id.cpp b/src/id.cpp index 7ec6491..463aa3d 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -1,32 +1,16 @@ -#include "id.hpp" +#include "ikarus/id.h" #include #include -uint64_t const IKARUS_ID_OBJECT_TYPE_BITS = 8; -uint64_t const IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; +constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; +constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; -auto from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { +auto ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); } auto ikarus_id_get_object_type(IkarusId id) -> IkarusObjectType { return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); } - -TEST_CASE("id_object_type", "[id]") { - // NOLINTNEXTLINE(readability-magic-numbers) - auto id = static_cast(IkarusObjectType_Blueprint) << IKARUS_ID_OBJECT_RANDOM_BITS; - - REQUIRE(ikarus_id_get_object_type(id) == IkarusObjectType_Blueprint); -} - -TEST_CASE("id_equal", "[id]") { - auto id = static_cast(IkarusObjectType_Blueprint) << IKARUS_ID_OBJECT_RANDOM_BITS; - auto copy = id; - auto third = static_cast(IkarusObjectType_Property) << IKARUS_ID_OBJECT_RANDOM_BITS; - - REQUIRE(ikarus_id_is_equal(id, copy)); - REQUIRE(!ikarus_id_is_equal(id, third)); -} diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index d6a38b1..3971d9e 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -8,9 +8,12 @@ #include #include -#include +#include #include +IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): + IkarusObject{project, id} {} + IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { LOG_INFO("creating new blueprint"); @@ -19,7 +22,7 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - auto * ctx = project->function_context(); + auto * ctx = project->get_function_context(); if (name == nullptr) { ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); @@ -31,12 +34,12 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - LOG_DEBUG("project={}; name={}", project->path().c_str(), name); + LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); VTRYRV( - auto id, + auto const id, nullptr, - project->db() + project->get_db() ->transact([name](auto * db) -> cppbase::Result { LOG_VERBOSE("creating blueprint in objects table"); @@ -79,14 +82,15 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { return; } - auto * ctx = blueprint->project->function_context(); + auto * ctx = blueprint->get_project()->get_function_context(); - LOG_DEBUG("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->get_id()); TRYRV( , - blueprint->project->db() - ->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id) + blueprint->get_project() + ->get_db() + ->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->get_id()) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to delete blueprint from objects table: {}", err), @@ -99,7 +103,7 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { LOG_VERBOSE("blueprint was successfully deleted from database, freeing blueprint"); - blueprint->project->remove_blueprint(blueprint); + blueprint->get_project()->uncache_blueprint(blueprint); LOG_VERBOSE("successfully deleted blueprint"); } @@ -112,15 +116,16 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { return 0; } - auto * ctx = blueprint->project->function_context(); + auto * ctx = blueprint->get_project()->get_function_context(); - LOG_DEBUG("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->get_id()); VTRYRV( auto count, 0, - blueprint->project->db() - ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->id) + blueprint->get_project() + ->get_db() + ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->get_id()) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch blueprint property count: {}", err), @@ -148,25 +153,26 @@ void ikarus_blueprint_get_properties( return; } - auto * ctx = blueprint->project->function_context(); + auto * ctx = blueprint->get_project()->get_function_context(); if (properties_out == nullptr) { ctx->set_error("properties_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return; } - LOG_DEBUG("blueprint={}; properties_out_size={}", blueprint->id, properties_out_size); + LOG_DEBUG("blueprint={}; properties_out_size={}", blueprint->get_id(), properties_out_size); IkarusId ids[properties_out_size]; TRYRV( , - blueprint->project->db() + blueprint->get_project() + ->get_db() ->query_many_buffered( "SELECT `id` FROM `properties` WHERE `source` = ?", static_cast(ids), properties_out_size, - blueprint->id + blueprint->get_id() ) .on_error([ctx](auto const& err) { ctx->set_error( @@ -182,7 +188,7 @@ void ikarus_blueprint_get_properties( for (size_t i = 0; i < properties_out_size; ++i) { /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - properties_out[i] = blueprint->project->get_property(ids[i]); + properties_out[i] = blueprint->get_project()->get_property(ids[i]); } LOG_VERBOSE("successfully fetched blueprint properties"); @@ -196,15 +202,16 @@ size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprin return 0; } - auto * ctx = blueprint->project->function_context(); + auto * ctx = blueprint->get_project()->get_function_context(); - LOG_DEBUG("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->get_id()); VTRYRV( auto count, 0, - blueprint->project->db() - ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->id) + blueprint->get_project() + ->get_db() + ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->get_id()) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch blueprint linked entity count: {}", err), @@ -232,25 +239,26 @@ void ikarus_blueprint_get_linked_entities( return; } - auto * ctx = blueprint->project->function_context(); + auto * ctx = blueprint->get_project()->get_function_context(); if (entities_out == nullptr) { ctx->set_error("entities_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return; } - LOG_DEBUG("blueprint={}; entities_out_size={}", blueprint->id, entities_out_size); + LOG_DEBUG("blueprint={}; entities_out_size={}", blueprint->get_id(), entities_out_size); IkarusId ids[entities_out_size]; TRYRV( , - blueprint->project->db() + blueprint->get_project() + ->get_db() ->query_many_buffered( "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", static_cast(ids), entities_out_size, - blueprint->id + blueprint->get_id() ) .on_error([ctx](auto const& err) { ctx->set_error( @@ -266,7 +274,7 @@ void ikarus_blueprint_get_linked_entities( for (size_t i = 0; i < entities_out_size; ++i) { /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - entities_out[i] = blueprint->project->get_entity(ids[i]); + entities_out[i] = blueprint->get_project()->get_entity(ids[i]); } LOG_VERBOSE("successfully fetched blueprint linked entities"); @@ -286,7 +294,7 @@ IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * bl // auto * ctx = blueprint->project->function_context(); - LOG_DEBUG("blueprint={}", blueprint->id); + LOG_DEBUG("blueprint={}", blueprint->get_id()); LOG_VERBOSE("successfully casted blueprint to object"); diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index f904074..445efdb 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -2,8 +2,14 @@ #include -/// \private -struct IkarusBlueprint : public IkarusObject { - inline IkarusBlueprint(struct IkarusProject * project, IkarusId id): - IkarusObject{project, id} {} +struct IkarusBlueprint final : IkarusObject { + inline IkarusBlueprint(struct IkarusProject * project, IkarusId id); + + IkarusBlueprint(IkarusBlueprint const&) = default; + IkarusBlueprint(IkarusBlueprint&&) = default; + + IkarusBlueprint& operator=(IkarusBlueprint const&) = default; + IkarusBlueprint& operator=(IkarusBlueprint&&) = default; + + ~IkarusBlueprint() override = default; }; diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index 28fb068..fb78e39 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -2,8 +2,15 @@ #include -/// \private -struct IkarusEntity : public IkarusObject { +struct IkarusEntity final : IkarusObject { inline IkarusEntity(struct IkarusProject * project, IkarusId id): IkarusObject{project, id} {} + + IkarusEntity(IkarusEntity const&) = default; + IkarusEntity(IkarusEntity&&) = default; + + IkarusEntity& operator=(IkarusEntity const&) = default; + IkarusEntity& operator=(IkarusEntity&&) = default; + + ~IkarusEntity() override = default; }; diff --git a/src/objects/object.cpp b/src/objects/object.cpp new file mode 100644 index 0000000..72aba6e --- /dev/null +++ b/src/objects/object.cpp @@ -0,0 +1,13 @@ +#include "object.hpp" + +IkarusProject * IkarusObject::get_project() { + return _project; +} + +IkarusProject * IkarusObject::get_project() const { + return _project; +} + +IkarusId IkarusObject::get_id() const { + return _id; +} diff --git a/src/objects/object.hpp b/src/objects/object.hpp index e95c3e0..ee1b147 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -1,14 +1,27 @@ #pragma once -#include - -#include +#include struct IkarusObject { - struct IkarusProject * project; - IkarusId id; +public: + IkarusObject(struct IkarusProject * project, IkarusId id); - inline IkarusObject(struct IkarusProject * project, IkarusId id): - project{project}, - id{id} {} + IkarusObject(IkarusObject const&) = default; + IkarusObject(IkarusObject&&) = default; + + IkarusObject& operator=(IkarusObject const&) = default; + IkarusObject& operator=(IkarusObject&&) = default; + + virtual ~IkarusObject() = default; + +public: + [[nodiscard]] inline struct IkarusProject * get_project(); + + [[nodiscard]] inline struct IkarusProject * get_project() const; + + [[nodiscard]] inline IkarusId get_id() const; + +private: + struct IkarusProject mutable * _project; + IkarusId _id; }; diff --git a/src/objects/properties/number_property.cpp b/src/objects/properties/number_property.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/properties/number_property.hpp b/src/objects/properties/number_property.hpp new file mode 100644 index 0000000..dd5f8a2 --- /dev/null +++ b/src/objects/properties/number_property.hpp @@ -0,0 +1,5 @@ +// +// Created by Jonathan Purol on 26.11.23. +// + +export module number_property.hpp; diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/properties/property.hpp b/src/objects/properties/property.hpp new file mode 100644 index 0000000..1a74577 --- /dev/null +++ b/src/objects/properties/property.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +struct IkarusProperty : IkarusObject { + IkarusProperty(struct IkarusProject * project, IkarusId id); + + IkarusProperty(IkarusProperty const&) = default; + IkarusProperty(IkarusProperty&&) = default; + + IkarusProperty& operator=(IkarusProperty const&) = default; + IkarusProperty& operator=(IkarusProperty&&) = default; + + ~IkarusProperty() override = default; +}; diff --git a/src/objects/properties/property_source.cpp b/src/objects/properties/property_source.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/properties/property_source.hpp b/src/objects/properties/property_source.hpp new file mode 100644 index 0000000..8da517a --- /dev/null +++ b/src/objects/properties/property_source.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include + +struct IkarusPropertySource { +public: + using Data = std::variant; + +public: + inline explicit IkarusPropertySource(Data data): + _data{data} {} + + IkarusPropertySource(IkarusPropertySource const&) = default; + IkarusPropertySource(IkarusPropertySource&&) = default; + + IkarusPropertySource& operator=(IkarusPropertySource const&) = default; + IkarusPropertySource& operator=(IkarusPropertySource&&) = default; + + ~IkarusPropertySource() = default; + +public: + [[nodiscard]] inline Data const& get_data() const { + return _data; + } + +private: + std::variant _data; +}; diff --git a/src/objects/properties/text_property.cpp b/src/objects/properties/text_property.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/properties/text_property.hpp b/src/objects/properties/text_property.hpp new file mode 100644 index 0000000..3816f35 --- /dev/null +++ b/src/objects/properties/text_property.hpp @@ -0,0 +1,8 @@ +// +// Created by Jonathan Purol on 26.11.23. +// + +#ifndef TEXT_PROPERTY_HPP +#define TEXT_PROPERTY_HPP + +#endif //TEXT_PROPERTY_HPP diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/objects/properties/toggle_property.hpp b/src/objects/properties/toggle_property.hpp new file mode 100644 index 0000000..5514d8b --- /dev/null +++ b/src/objects/properties/toggle_property.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include + +struct IkarusToggleProperty final : IkarusProperty { + IkarusToggleProperty(struct IkarusProject * project, IkarusId id); +}; diff --git a/src/objects/property.cpp b/src/objects/property.cpp deleted file mode 100644 index 80d9031..0000000 --- a/src/objects/property.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "property.hpp" - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include diff --git a/src/objects/property.hpp b/src/objects/property.hpp deleted file mode 100644 index b59d7dd..0000000 --- a/src/objects/property.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -/// \private -struct IkarusProperty : public IkarusObject { - inline IkarusProperty(struct IkarusProject * project, IkarusId id): - IkarusObject{project, id} {} -}; diff --git a/src/objects/property_source.hpp b/src/objects/property_source.hpp deleted file mode 100644 index e2a2739..0000000 --- a/src/objects/property_source.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -#include - -/// \private -struct IkarusPropertySource { - std::variant data; -}; diff --git a/src/persistence/function_context.cpp b/src/persistence/function_context.cpp new file mode 100644 index 0000000..6e252dc --- /dev/null +++ b/src/persistence/function_context.cpp @@ -0,0 +1,18 @@ +#include "function_context.hpp" + +FunctionContext::FunctionContext(IkarusProject * project): + _project{project} {} + +FunctionContext::~FunctionContext() { + if (_project->_function_contexts.size() == 1) { + if (_project->error_message_buffer.empty()) { + _project->error_message_buffer.push_back('\0'); + } else { + _project->error_message_buffer[0] = '\0'; + } + + _project->error_infos = {}; + } + + _project->_function_contexts.pop(); +} diff --git a/src/persistence/function_context.hpp b/src/persistence/function_context.hpp new file mode 100644 index 0000000..3dc64b3 --- /dev/null +++ b/src/persistence/function_context.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +#include + +#include + +struct FunctionContext { +public: + explicit FunctionContext(struct IkarusProject * project); + + FunctionContext(FunctionContext const&) noexcept = default; + FunctionContext(FunctionContext&&) noexcept = default; + + auto operator=(FunctionContext const&) noexcept -> FunctionContext& = default; + auto operator=(FunctionContext&&) noexcept -> FunctionContext& = default; + + ~FunctionContext(); + +public: + template + requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) + auto set_error(std::string_view error_message, bool log_error, Infos... infos) -> void { + if (error_message.size() > _project->error_message_buffer.size()) { + _project->error_message_buffer.resize(error_message.size() + 1); + } + + for (int i = 0; i < error_message.size(); ++i) { + _project->error_message_buffer[i] = error_message[i]; + } + + _project->error_message_buffer[error_message.size()] = '\0'; + _project->error_infos = {infos...}; + + if (log_error) { + LOG_ERROR( + "Error({}): {}", + fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), + error_message + ); + } + } + +private: + struct IkarusProject * _project; +}; diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp new file mode 100644 index 0000000..3ceb821 --- /dev/null +++ b/src/persistence/project.cpp @@ -0,0 +1,49 @@ +#include "project.hpp" + +#include "ikarus/persistence/project.h" + +#include + +auto IkarusProject::get_name() const -> std::string_view { + return _name; +} + +auto IkarusProject::get_path() const -> std::filesystem::path const& { + return _path; +} + +auto IkarusProject::get_db() -> sqlitecpp::Connection * { + return _db.get(); +} + +auto IkarusProject::get_db() const -> sqlitecpp::Connection const * { + return _db.get(); +} + +auto IkarusProject::get_function_context() -> FunctionContext * { + return &_function_contexts.emplace(this); +} + +IkarusBlueprint * IkarusProject::get_blueprint(IkarusId id) { + return get_cached_object(id, this->_blueprints); +} + +auto IkarusProject::uncache_blueprint(IkarusBlueprint * blueprint) -> void { + remove_cached_object(blueprint, _blueprints); +} + +auto IkarusProject::get_entity(IkarusId id) -> IkarusEntity * { + return get_cached_object(id, this->_entities); +} + +auto IkarusProject::uncache_entity(IkarusEntity * entity) -> void { + remove_cached_object(entity, _entities); +} + +auto IkarusProject::get_property(IkarusId id) -> IkarusProperty * { + return get_cached_object(id, this->_properties); +} + +auto IkarusProject::uncache_property(IkarusProperty * property) -> void { + remove_cached_object(property, _properties); +} diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index fbb84f2..ef69a81 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -9,83 +8,45 @@ #include #include +#include -constexpr inline size_t MAXIMUM_ERROR_INFOS = 8; -constexpr inline size_t MAXIMUM_ERROR_MESSAGE_LENGTH = 256; - -/// \private -class FunctionContext { -public: - explicit FunctionContext(struct IkarusProject * project); - FunctionContext(FunctionContext const&) noexcept = default; - FunctionContext(FunctionContext&&) noexcept = default; - - auto operator=(FunctionContext const&) noexcept -> FunctionContext& = default; - auto operator=(FunctionContext&&) noexcept -> FunctionContext& = default; - - ~FunctionContext(); - - template - requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) - auto set_error(std::string_view error_message, bool log_error, Infos... infos) -> void; - -private: - struct IkarusProject * _project; -}; +constexpr inline auto MAXIMUM_ERROR_INFOS = 8; /// \private struct IkarusProject { public: - [[nodiscard]] inline auto name() const -> std::string_view { - return _name; - } + [[nodiscard]] auto get_name() const -> std::string_view; - [[nodiscard]] inline auto path() const -> std::filesystem::path const& { - return _path; - } + [[nodiscard]] auto get_path() const -> std::filesystem::path const&; - [[nodiscard]] inline auto db() -> sqlitecpp::Connection * { - return _db.get(); - } + [[nodiscard]] auto get_db() -> sqlitecpp::Connection *; + [[nodiscard]] auto get_db() const -> sqlitecpp::Connection const *; - inline auto function_context() -> FunctionContext * { - return &_function_contexts.emplace(this); - } +public: + [[nodiscard]] auto get_function_context() -> struct FunctionContext *; - [[nodiscard]] IkarusBlueprint * get_blueprint(IkarusId id) { - return get_cached_object(id, this->_blueprints); - } +public: + [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; + auto uncache_blueprint(struct IkarusBlueprint * blueprint) -> void; - auto remove_blueprint(IkarusBlueprint * blueprint) -> void { - remove_cached_object(blueprint, _blueprints); - } + [[nodiscard]] auto get_entity(IkarusId id) -> struct IkarusEntity *; + auto uncache_entity(struct IkarusEntity * entity) -> void; - [[nodiscard]] auto get_entity(IkarusId id) -> IkarusEntity * { - return get_cached_object(id, this->_entities); - } - - auto remove_entity(IkarusEntity * entity) -> void { - remove_cached_object(entity, _entities); - } - - [[nodiscard]] auto get_property(IkarusId id) -> IkarusProperty * { - return get_cached_object(id, this->_properties); - } - - auto remove_property(IkarusProperty * property) -> void { - remove_cached_object(property, _properties); - } + [[nodiscard]] auto get_property(IkarusId id) -> struct IkarusProperty *; + auto uncache_property(struct IkarusProperty * property) -> void; private: template [[nodiscard]] T * get_cached_object(IkarusId id, std::unordered_map>& cache) { - if (auto iter = cache.find(id); iter == cache.cend()) { + auto const iter = cache.find(id); + + if (iter == cache.cend()) { auto [ret_iter, _] = cache.emplace(id, std::make_unique(this, id)); return ret_iter->second.get(); - } else { - return iter->second.get(); } + + return iter->second.get(); } template @@ -94,7 +55,7 @@ private: } private: - friend class FunctionContext; + friend struct FunctionContext; std::string _name; std::filesystem::path _path; @@ -109,41 +70,3 @@ private: std::stack _function_contexts; }; - -FunctionContext::FunctionContext(struct IkarusProject * project): - _project{project} {} - -FunctionContext::~FunctionContext() { - if (_project->_function_contexts.size() == 1) { - if (_project->error_message_buffer.empty()) { - _project->error_message_buffer.push_back('\0'); - } else { - _project->error_message_buffer[0] = '\0'; - } - - _project->error_infos = {}; - } - - _project->_function_contexts.pop(); -} - -template - requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) -auto FunctionContext::set_error(std::string_view error_message, bool log_error, Infos... infos) -> void { - if (error_message.size() > _project->error_message_buffer.size()) { - _project->error_message_buffer.resize(error_message.size() + 1); - } - - for (int i = 0; i < error_message.size(); ++i) { - _project->error_message_buffer[i] = error_message[i]; - } - - _project->error_message_buffer[error_message.size()] = '\0'; - _project->error_infos = {infos...}; - - if (log_error) { - LOG_ERROR( - "Error({}): {}", fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), error_message - ); - } -} diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 3057656..538616d 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 3057656ff277294ab424af90e553e630c2a5e8f7 +Subproject commit 538616d8ced2d3f04659261ccae3b039d65da004 From 92d97861e6df86412c1fab801fac3b2c0cb10a49 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 27 Nov 2023 13:13:35 +0100 Subject: [PATCH 101/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 538616d..07b806f 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 538616d8ced2d3f04659261ccae3b039d65da004 +Subproject commit 07b806f4d73b27a555a3ba82a97d89913e430e48 From 78a15b759dba2bf9b9a047aa061a1e11a0d43871 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 27 Nov 2023 13:13:54 +0100 Subject: [PATCH 102/166] fixup compile errors and allow fetching property properly from cache Signed-off-by: Folling --- include/ikarus/objects/object_type.h | 8 +- .../objects/properties/number_property.h | 1 - .../ikarus/objects/properties/text_property.h | 1 - .../objects/properties/toggle_property.h | 1 - src/objects/blueprint.cpp | 99 +++++++------------ src/objects/blueprint.hpp | 2 +- src/objects/object.cpp | 4 + src/objects/object.hpp | 6 +- src/objects/properties/number_property.cpp | 4 + src/objects/properties/number_property.hpp | 11 ++- src/objects/properties/property.cpp | 57 +++++++++++ src/objects/properties/property.hpp | 23 ++++- src/objects/properties/text_property.cpp | 4 + src/objects/properties/text_property.hpp | 12 +-- src/objects/properties/toggle_property.cpp | 6 ++ src/objects/properties/toggle_property.hpp | 1 + src/persistence/function_context.cpp | 2 +- src/persistence/project.cpp | 26 ++++- src/persistence/project.hpp | 17 ++-- src/values/value.cpp | 25 ++++- src/values/value.hpp | 25 ++--- 21 files changed, 213 insertions(+), 122 deletions(-) diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 578919a..efb6609 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -15,11 +15,11 @@ enum IkarusObjectType { /// \brief Not an object or no object. IkarusObjectType_None = 0, /// \brief An IkarusEntity. - IkarusObjectType_Entity = 0b00000011, - /// \brief An IkarusProperty. - IkarusObjectType_Property = 0b00000010, + IkarusObjectType_Entity = 0b00000001, /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 0b00000001, + IkarusObjectType_Blueprint = 0b00000010, + /// \brief An IkarusProperty. + IkarusObjectType_Property = 0b00000011, }; IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 7787cc6..9b4fa4d 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -21,7 +21,6 @@ IKA_API IkarusNumberProperty * ikarus_number_property_create( /// \param property The number property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \remark The settings take ownership of the value, the caller must not free it. IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(struct IkarusNumberProperty * property); /// \brief Sets the default value for a number property. diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index ad90bf6..0e6a435 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -21,7 +21,6 @@ IKA_API IkarusTextProperty * ikarus_text_property_create( /// \param property The text property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \remark The settings take ownership of the value, the caller must not free it. IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property); /// \brief Sets the default value for a text property. diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index 9e8119d..0d67304 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -21,7 +21,6 @@ IKA_API IkarusToggleProperty * ikarus_toggle_property_create( /// \param property The toggle property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \remark The settings take ownership of the value, the caller must not free it. IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property); /// \brief Sets the default value for a toggle property. diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index 3971d9e..bdbea93 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -17,25 +17,15 @@ IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { LOG_INFO("creating new blueprint"); - if (project == nullptr) { - LOG_ERROR("project is nullptr"); - return nullptr; - } + LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); auto * ctx = project->get_function_context(); - if (name == nullptr) { - ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - if (cppbase::is_empty_or_blank(name)) { ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); return nullptr; } - LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); - VTRYRV( auto const id, nullptr, @@ -77,15 +67,10 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { LOG_INFO("deleting blueprint"); - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return; - } + LOG_DEBUG("project={};blueprint={}", blueprint->get_project()->get_path().c_str(), blueprint->get_id()); auto * ctx = blueprint->get_project()->get_function_context(); - LOG_DEBUG("blueprint={}", blueprint->get_id()); - TRYRV( , blueprint->get_project() @@ -101,7 +86,7 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { }) ); - LOG_VERBOSE("blueprint was successfully deleted from database, freeing blueprint"); + LOG_VERBOSE("blueprint was successfully deleted from database, freeing"); blueprint->get_project()->uncache_blueprint(blueprint); @@ -111,11 +96,6 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { LOG_VERBOSE("fetching blueprint property count"); - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return 0; - } - auto * ctx = blueprint->get_project()->get_function_context(); LOG_DEBUG("blueprint={}", blueprint->get_id()); @@ -148,20 +128,15 @@ void ikarus_blueprint_get_properties( ) { LOG_VERBOSE("fetching blueprint properties"); - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return; - } + LOG_DEBUG( + "project={};blueprint={};properties_out_size={}", + blueprint->get_project()->get_path().c_str(), + blueprint->get_id(), + properties_out_size + ); auto * ctx = blueprint->get_project()->get_function_context(); - if (properties_out == nullptr) { - ctx->set_error("properties_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return; - } - - LOG_DEBUG("blueprint={}; properties_out_size={}", blueprint->get_id(), properties_out_size); - IkarusId ids[properties_out_size]; TRYRV( @@ -187,8 +162,23 @@ void ikarus_blueprint_get_properties( LOG_DEBUG("blueprint properties: [{}]", fmt::join(ids, ids + properties_out_size, ", ")); for (size_t i = 0; i < properties_out_size; ++i) { + auto const id = ids[i]; + + VTRYRV( + auto const type, + , + IkarusProperty::get_property_type(blueprint->get_project(), id).on_error([ctx, id](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch property {}'s type: {}", id, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - properties_out[i] = blueprint->get_project()->get_property(ids[i]); + properties_out[i] = blueprint->get_project()->get_property(id, type); } LOG_VERBOSE("successfully fetched blueprint properties"); @@ -197,15 +187,10 @@ void ikarus_blueprint_get_properties( size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { LOG_VERBOSE("fetching blueprint linked entity count"); - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return 0; - } + LOG_DEBUG("project={};blueprint={}", blueprint->get_project()->get_path().c_str(), blueprint->get_id()); auto * ctx = blueprint->get_project()->get_function_context(); - LOG_DEBUG("blueprint={}", blueprint->get_id()); - VTRYRV( auto count, 0, @@ -234,20 +219,15 @@ void ikarus_blueprint_get_linked_entities( ) { LOG_VERBOSE("fetching blueprint linked entities"); - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return; - } + LOG_DEBUG( + "project={};blueprint={};entities_out_size={}", + blueprint->get_project()->get_path().c_str(), + blueprint->get_id(), + entities_out_size + ); auto * ctx = blueprint->get_project()->get_function_context(); - if (entities_out == nullptr) { - ctx->set_error("entities_out is null", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return; - } - - LOG_DEBUG("blueprint={}; entities_out_size={}", blueprint->get_id(), entities_out_size); - IkarusId ids[entities_out_size]; TRYRV( @@ -281,22 +261,9 @@ void ikarus_blueprint_get_linked_entities( } IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint) { - return const_cast(ikarus_blueprint_to_object_const(blueprint)); + return static_cast(blueprint); } IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint) { - LOG_VERBOSE("casting blueprint to object"); - - if (blueprint == nullptr) { - LOG_ERROR("blueprint is nullptr"); - return nullptr; - } - - // auto * ctx = blueprint->project->function_context(); - - LOG_DEBUG("blueprint={}", blueprint->get_id()); - - LOG_VERBOSE("successfully casted blueprint to object"); - return static_cast(blueprint); } diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index 445efdb..41042ba 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -3,7 +3,7 @@ #include struct IkarusBlueprint final : IkarusObject { - inline IkarusBlueprint(struct IkarusProject * project, IkarusId id); + IkarusBlueprint(struct IkarusProject * project, IkarusId id); IkarusBlueprint(IkarusBlueprint const&) = default; IkarusBlueprint(IkarusBlueprint&&) = default; diff --git a/src/objects/object.cpp b/src/objects/object.cpp index 72aba6e..2ce961e 100644 --- a/src/objects/object.cpp +++ b/src/objects/object.cpp @@ -1,5 +1,9 @@ #include "object.hpp" +IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): + _project{project}, + _id{id} {} + IkarusProject * IkarusObject::get_project() { return _project; } diff --git a/src/objects/object.hpp b/src/objects/object.hpp index ee1b147..bb28da5 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -15,11 +15,11 @@ public: virtual ~IkarusObject() = default; public: - [[nodiscard]] inline struct IkarusProject * get_project(); + [[nodiscard]] struct IkarusProject * get_project(); - [[nodiscard]] inline struct IkarusProject * get_project() const; + [[nodiscard]] struct IkarusProject * get_project() const; - [[nodiscard]] inline IkarusId get_id() const; + [[nodiscard]] IkarusId get_id() const; private: struct IkarusProject mutable * _project; diff --git a/src/objects/properties/number_property.cpp b/src/objects/properties/number_property.cpp index e69de29..d06cfb5 100644 --- a/src/objects/properties/number_property.cpp +++ b/src/objects/properties/number_property.cpp @@ -0,0 +1,4 @@ +#include "number_property.hpp" + +IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, IkarusId id): + IkarusProperty{project, id, this} {} diff --git a/src/objects/properties/number_property.hpp b/src/objects/properties/number_property.hpp index dd5f8a2..df2ab7e 100644 --- a/src/objects/properties/number_property.hpp +++ b/src/objects/properties/number_property.hpp @@ -1,5 +1,8 @@ -// -// Created by Jonathan Purol on 26.11.23. -// +#pragma once -export module number_property.hpp; +#include + +struct IkarusNumberProperty final : IkarusProperty { +public: + IkarusNumberProperty(struct IkarusProject * project, IkarusId id); +}; diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index e69de29..3afcf66 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -0,0 +1,57 @@ +#include "property.hpp" + +#include + +#include + +#include +#include + +IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): + IkarusObject{project, id}, + _data{data} {} + +cppbase::Result IkarusProperty::get_property_type( + IkarusProject * project, IkarusId id +) { + LOG_DEBUG("fetching unboxed property type"); + + LOG_VERBOSE("project={};property={}", project->get_path().c_str(), id); + + VTRY(auto const type, project->get_db()->query_one("SELECT `type` FROM `objects` WHERE `id` = ?", id)); + + return cppbase::ok(static_cast(type)); +} + +IKA_API void ikarus_property_delete(IkarusProperty * property) { + LOG_INFO("deleting property"); + + LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + + auto * ctx = property->get_project()->get_function_context(); + + TRYRV( + , + property->get_project() + ->get_db() + ->execute("DELETE FROM `objects` WHERE `id` = ?", property->get_id()) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to delete property from objects table: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_VERBOSE("property was successfully deleted from database, freeing"); + + property->get_project()->uncache_property(property); + + LOG_VERBOSE("successfully deleted property"); +} + +IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { + LOG_DEBUG("fetching property type"); +} diff --git a/src/objects/properties/property.hpp b/src/objects/properties/property.hpp index 1a74577..b11a8ff 100644 --- a/src/objects/properties/property.hpp +++ b/src/objects/properties/property.hpp @@ -1,9 +1,27 @@ #pragma once +#include + +#include + +#include + +#include + #include struct IkarusProperty : IkarusObject { - IkarusProperty(struct IkarusProject * project, IkarusId id); +public: + using Data = std::variant; + +public: + /// \brief Helper to fetch a type for a property that isn't yet wrapped in an object + [[nodiscard]] static cppbase::Result get_property_type( + struct IkarusProject * project, IkarusId id + ); + +public: + IkarusProperty(struct IkarusProject * project, IkarusId id, Data data); IkarusProperty(IkarusProperty const&) = default; IkarusProperty(IkarusProperty&&) = default; @@ -12,4 +30,7 @@ struct IkarusProperty : IkarusObject { IkarusProperty& operator=(IkarusProperty&&) = default; ~IkarusProperty() override = default; + +private: + Data _data; }; diff --git a/src/objects/properties/text_property.cpp b/src/objects/properties/text_property.cpp index e69de29..fd64154 100644 --- a/src/objects/properties/text_property.cpp +++ b/src/objects/properties/text_property.cpp @@ -0,0 +1,4 @@ +#include "text_property.hpp" + +IkarusTextProperty::IkarusTextProperty(IkarusProject * project, IkarusId id): + IkarusProperty{project, id, this} {} diff --git a/src/objects/properties/text_property.hpp b/src/objects/properties/text_property.hpp index 3816f35..3a5d163 100644 --- a/src/objects/properties/text_property.hpp +++ b/src/objects/properties/text_property.hpp @@ -1,8 +1,8 @@ -// -// Created by Jonathan Purol on 26.11.23. -// +#pragma once -#ifndef TEXT_PROPERTY_HPP -#define TEXT_PROPERTY_HPP +#include -#endif //TEXT_PROPERTY_HPP +struct IkarusTextProperty final : IkarusProperty { +public: + IkarusTextProperty(struct IkarusProject * project, IkarusId id); +}; diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp index e69de29..d2fb82d 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/objects/properties/toggle_property.cpp @@ -0,0 +1,6 @@ +#include "toggle_property.hpp" + +IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): + IkarusProperty{project, id, this} { + +} diff --git a/src/objects/properties/toggle_property.hpp b/src/objects/properties/toggle_property.hpp index 5514d8b..2ca2520 100644 --- a/src/objects/properties/toggle_property.hpp +++ b/src/objects/properties/toggle_property.hpp @@ -3,5 +3,6 @@ #include struct IkarusToggleProperty final : IkarusProperty { +public: IkarusToggleProperty(struct IkarusProject * project, IkarusId id); }; diff --git a/src/persistence/function_context.cpp b/src/persistence/function_context.cpp index 6e252dc..96edddf 100644 --- a/src/persistence/function_context.cpp +++ b/src/persistence/function_context.cpp @@ -14,5 +14,5 @@ FunctionContext::~FunctionContext() { _project->error_infos = {}; } - _project->_function_contexts.pop(); + _project->_function_contexts.pop_back(); } diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index 3ceb821..d7d5e5e 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -2,6 +2,9 @@ #include "ikarus/persistence/project.h" +#include +#include +#include #include auto IkarusProject::get_name() const -> std::string_view { @@ -21,11 +24,11 @@ auto IkarusProject::get_db() const -> sqlitecpp::Connection const * { } auto IkarusProject::get_function_context() -> FunctionContext * { - return &_function_contexts.emplace(this); + return &_function_contexts.emplace_back(this); } IkarusBlueprint * IkarusProject::get_blueprint(IkarusId id) { - return get_cached_object(id, this->_blueprints); + return get_cached_object(id, this->_blueprints); } auto IkarusProject::uncache_blueprint(IkarusBlueprint * blueprint) -> void { @@ -33,15 +36,28 @@ auto IkarusProject::uncache_blueprint(IkarusBlueprint * blueprint) -> void { } auto IkarusProject::get_entity(IkarusId id) -> IkarusEntity * { - return get_cached_object(id, this->_entities); + return get_cached_object(id, this->_entities); } auto IkarusProject::uncache_entity(IkarusEntity * entity) -> void { remove_cached_object(entity, _entities); } -auto IkarusProject::get_property(IkarusId id) -> IkarusProperty * { - return get_cached_object(id, this->_properties); +auto IkarusProject::get_property(IkarusId id, IkarusPropertyType type) -> IkarusProperty * { + auto const iter = _properties.find(id); + + if (iter == _properties.cend()) { + switch (type) { + case IkarusPropertyType_Toggle: + return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); + case IkarusPropertyType_Number: + return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); + case IkarusPropertyType_Text: + return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); + } + } + + return iter->second.get(); } auto IkarusProject::uncache_property(IkarusProperty * property) -> void { diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index ef69a81..d3fc6a1 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -9,6 +9,11 @@ #include #include +#include + +#include +#include +#include constexpr inline auto MAXIMUM_ERROR_INFOS = 8; @@ -32,18 +37,16 @@ public: [[nodiscard]] auto get_entity(IkarusId id) -> struct IkarusEntity *; auto uncache_entity(struct IkarusEntity * entity) -> void; - [[nodiscard]] auto get_property(IkarusId id) -> struct IkarusProperty *; + [[nodiscard]] auto get_property(IkarusId id, IkarusPropertyType type) -> struct IkarusProperty *; auto uncache_property(struct IkarusProperty * property) -> void; private: template - [[nodiscard]] T * get_cached_object(IkarusId id, std::unordered_map>& cache) { + [[nodiscard]] T * get_cached_object(IkarusId id, auto& cache) { auto const iter = cache.find(id); if (iter == cache.cend()) { - auto [ret_iter, _] = cache.emplace(id, std::make_unique(this, id)); - - return ret_iter->second.get(); + return cache.emplace(id, std::make_unique(this, id)).first->second.get(); } return iter->second.get(); @@ -51,7 +54,7 @@ private: template void remove_cached_object(T * object, std::unordered_map>& cache) { - cache.erase(object->id); + cache.erase(object->get_id()); } private: @@ -68,5 +71,5 @@ private: std::unordered_map> _properties; std::unordered_map> _entities; - std::stack _function_contexts; + std::vector _function_contexts; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index f61f2bb..7528ec8 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -9,6 +9,25 @@ #include #include +IkarusValue::IkarusValue(Data data): + _data(data) {} + +bool IkarusValue::is_interminate() const { + return _indeterminate; +} + +void IkarusValue::set_intermediate(bool value) { + _indeterminate = value; +} + +IkarusValue::Data& IkarusValue::get_data() { + return _data; +} + +IkarusValue::Data const& IkarusValue::get_data() const { + return _data; +} + bool ikarus_value_is_indeterminate(IkarusValue const * value) { return value->is_interminate(); } @@ -20,9 +39,9 @@ void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { char const * ikarus_value_to_string(IkarusValue const * value) { auto const str = std::visit( cppbase::overloaded{ - [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->value ? "true" : "false"; }, - [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->value); }, - [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->value); }, + [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->get_value() ? "true" : "false"; }, + [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->get_value()); }, + [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->get_value()); }, }, value->get_data() ); diff --git a/src/values/value.hpp b/src/values/value.hpp index 0adc61c..ddf6601 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -4,11 +4,10 @@ struct IkarusValue { public: - using Data = std::variant::variant; + using Data = std::variant; public: - explicit IkarusValue(Data data): - _data(data) {} + explicit IkarusValue(Data data); IkarusValue(IkarusValue const&) = default; IkarusValue(IkarusValue&&) noexcept = default; @@ -16,24 +15,14 @@ public: IkarusValue& operator=(IkarusValue const&) = default; IkarusValue& operator=(IkarusValue&&) noexcept = default; - virtual ~IkarusValue(); + virtual ~IkarusValue() = default; public: - [[nodiscard]] inline bool is_interminate() const { - return _indeterminate; - } + [[nodiscard]] bool is_interminate() const; + void set_intermediate(bool value); - void set_intermediate(bool value) { - _indeterminate = value; - } - - [[nodiscard]] inline Data& get_data() { - return _data; - } - - [[nodiscard]] inline Data const& get_data() const { - return _data; - } + [[nodiscard]] Data& get_data(); + [[nodiscard]] Data const& get_data() const; private: bool _indeterminate{false}; From 43b53fd565a3bd224a2411d90d4dcf36f2bddc9a Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 27 Nov 2023 13:37:42 +0100 Subject: [PATCH 103/166] implement property (base) logic Signed-off-by: Folling --- include/ikarus/objects/properties/property.h | 18 +-- src/objects/properties/property.cpp | 111 ++++++++++++++++++- src/objects/properties/property.hpp | 4 + 3 files changed, 122 insertions(+), 11 deletions(-) diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index 1da35e6..a6ebad8 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -82,24 +82,24 @@ IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusPro /// \param property The property to visit. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param toggle_property The function to call if the property is a toggle property. Skipped if null. -/// \param number_property The function to call if the property is a number property. Skipped if null. -/// \param text_property The function to call if the property is a text property. Skipped if null. +/// \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. IKA_API void ikarus_property_visit( IkarusProperty * property, - void (*toggle_property)(struct IkarusToggleProperty *, void *), - void (*number_property)(struct IkarusNumberProperty *, void *), - void (*text_property)(struct IkarusTextProperty *, void *), + void (*toggle_property_visitor)(struct IkarusToggleProperty *, void *), + void (*number_property_visitor)(struct IkarusNumberProperty *, void *), + void (*text_property_visitor)(struct IkarusTextProperty *, void *), void * data ); /// \see ikarus_property_visit IKA_API void ikarus_property_visit_const( IkarusProperty const * property, - void (*toggle_property)(struct IkarusToggleProperty const *, void *), - void (*number_property)(struct IkarusNumberProperty const *, void *), - void (*text_property)(struct IkarusTextProperty const *, void *), + 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 ); diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index 3afcf66..cda8434 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -4,13 +4,23 @@ #include +#include #include #include +#include IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): IkarusObject{project, id}, _data{data} {} +IkarusProperty::Data& IkarusProperty::get_data() { + return _data; +} + +IkarusProperty::Data const& IkarusProperty::get_data() const { + return _data; +} + cppbase::Result IkarusProperty::get_property_type( IkarusProject * project, IkarusId id ) { @@ -18,7 +28,21 @@ cppbase::Result IkarusProperty: LOG_VERBOSE("project={};property={}", project->get_path().c_str(), id); - VTRY(auto const type, project->get_db()->query_one("SELECT `type` FROM `objects` WHERE `id` = ?", id)); + auto * ctx = project->get_function_context(); + + VTRY( + auto const type, + project->get_db() + ->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch unboxed property type: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); return cppbase::ok(static_cast(type)); } @@ -53,5 +77,88 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { } IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { - LOG_DEBUG("fetching property type"); + LOG_VERBOSE("fetching property type"); + + return IkarusProperty::get_property_type(property->get_project(), property->get_id()) + .unwrap_value_or(IkarusPropertyType_Toggle); +} + +IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { + LOG_VERBOSE("fetching property source"); + + LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + + auto * ctx = property->get_project()->get_function_context(); + + VTRY( + auto const source, + property->get_project() + ->get_db() + ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->get_id()) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch property's source: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + switch (ikarus_id_get_object_type(source)) { + case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->get_project()->get_blueprint(source)}; + case IkarusObjectType_Entity: return new IkarusPropertySource{property->get_project()->get_entity(source)}; + default: { + ctx->set_error( + fmt::format("PropertySource is neither blueprint nor entity"), + true, + IkarusErrorInfo_Source_LibIkarus, + IkarusErrorInfo_Type_LibIkarus_InvalidState + ); + + return nullptr; + } + } +} + +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 +) { + 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->get_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 +) { + 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->get_data() + ); +} + +IkarusObject * ikarus_property_to_object(IkarusProperty * property) { + return static_cast(property); +} + +IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property) { + return static_cast(property); } diff --git a/src/objects/properties/property.hpp b/src/objects/properties/property.hpp index b11a8ff..6acd377 100644 --- a/src/objects/properties/property.hpp +++ b/src/objects/properties/property.hpp @@ -31,6 +31,10 @@ public: ~IkarusProperty() override = default; +public: + [[nodiscard]] Data& get_data(); + [[nodiscard]] Data const& get_data() const; + private: Data _data; }; From 2e41c36d84c05f062e8f7b629ed05f4a36146597 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 28 Nov 2023 11:05:53 +0100 Subject: [PATCH 104/166] make values capable of being a list & add boost Signed-off-by: Folling --- .clang-format | 20 +++---- CMakeLists.txt | 8 +++ include/ikarus/objects/properties/property.h | 8 +++ include/ikarus/values/number_value.h | 49 +++++++++++------ include/ikarus/values/text_value.h | 50 ++++++++++------- include/ikarus/values/toggle_value.h | 49 +++++++++++------ include/ikarus/values/value.h | 11 ++-- src/objects/properties/property.cpp | 26 +++++++++ src/objects/properties/toggle_property.cpp | 6 ++- .../migrations/m1_initial_layout.sql | 17 +++--- src/values/number_value.cpp | 47 ++++++++++++---- src/values/number_value.hpp | 20 +++---- src/values/text_value.cpp | 47 ++++++++++++---- src/values/text_value.hpp | 23 ++++---- src/values/toggle_value.cpp | 54 +++++++++++++++---- src/values/toggle_value.hpp | 20 +++---- 16 files changed, 315 insertions(+), 140 deletions(-) diff --git a/.clang-format b/.clang-format index 513423e..23c71e6 100644 --- a/.clang-format +++ b/.clang-format @@ -87,24 +87,26 @@ IncludeCategories: Priority: 2 - Regex: '^<[a-z0-9_]+>$' Priority: 3 - - Regex: '^$' + - Regex: '^$' Priority: 4 - - Regex: '^$' + - Regex: '^$' Priority: 5 - - Regex: '^$' + - Regex: '^$' Priority: 6 - - Regex: '^$' + - Regex: '^$' Priority: 7 - - Regex: '^$' + - Regex: '^$' Priority: 8 - - Regex: '^$' + - Regex: '^$' Priority: 9 - - Regex: '^$' + - Regex: '^$' Priority: 10 - - Regex: '^$' + - Regex: '^$' Priority: 11 - - Regex: '^$' + - Regex: '^$' Priority: 12 + - Regex: '^$' + Priority: 13 IndentAccessModifiers: false IndentCaseBlocks: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bbb826..ae81667 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,8 @@ add_subdirectory(vendor) add_subdirectory(include) add_subdirectory(src) +find_package(Boost REQUIRED) + add_library( libikarus SHARED ${INCLUDE_FILES} @@ -34,6 +36,12 @@ target_link_libraries( libikarus PRIVATE cppbase sqlitecpp + ${Boost_LIBRARIES} +) + +target_include_directories( + libikarus PRIVATE + ${Boost_INCLUDE_DIRS} ) if (LIBIKARUS_ENABLE_LINTS) diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index a6ebad8..7aa6a2a 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -78,6 +78,14 @@ IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * prope /// \remark Must be freed using #ikarus_free. IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property); +/// \brief Gets the default value of a property. +/// \param property The property to get the type info of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The default value of the property or null if an error occurs. +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); + /// \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. diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index d4b6446..2edb69b 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -4,42 +4,57 @@ /// \author Folling #include +#include /// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER -/// \brief A true/false boolean-like value. For example "IsDead". +/// \brief A true/false long doubleean-esque value. For example "Is Dead". struct IkarusNumberValue; -/// \brief Creates a number value from a long double. -/// \param value The numeric value. -/// \return The entity value. +/// \brief Creates a number value from doubles. +/// \param data The number data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param data_size The size of the data array or 0 if you wish to clear the data. +/// \return The value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create(long double value); +IKA_API IkarusNumberValue * ikarus_number_value_create(long double * data, size_t data_size); /// \brief Creates an indeterminate number value. -/// \return The entity value. +/// \return The value. /// \remark Must be freed with #ikarus_free. IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); -/// \brief Fetches the underlying value of a number value. -/// \param value The number value. -/// \return The underlying value. -/// \warning Undefined if the value is indeterminate. -IKA_API long double ikarus_number_value_get(IkarusNumberValue const * value); - -/// \brief Sets the value of a number value. +/// \brief Fetches the underlying data of a number value. +/// \details You may adjust the returned data as per your hearts desire. +/// If you need to grow the data, use #ikarus_number_value_set with a new array. +/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. +/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. /// \param value The number value. /// \pre \li Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value); +/// \param data_size_out An out-parameter for the size of the returned array. +/// \remark Ignored if null. +/// \return The underlying data. Owned by LibIkarus, most not be freed. +/// \warning Undefined if the value is indeterminate. +IKA_API long double * ikarus_number_value_get(IkarusNumberValue * value, size_t * data_size_out); + +/// \see ikarus_number_value_get +long double const * ikarus_number_value_get_const(IkarusNumberValue const * value, size_t * data_size_out); + +/// \brief Sets the data of a number value. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param new_data The new data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, long double * new_data, size_t new_data_size); /// \brief Converts a number value to an entity value. -/// \param number_value The number value to convert. +/// \param value The number value to convert. /// \return The converted entity value. -IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * number_value); +IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value); IKARUS_END_HEADER diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index 8562ab5..3f1f682 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -4,43 +4,57 @@ /// \author Folling #include +#include /// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER -/// \brief A true/false boolean-like value. For example "IsDead". +/// \brief A true/false char const*ean-esque value. For example "Is Dead". struct IkarusTextValue; -/// \brief Creates a text value from a boolean. -/// \param value The text value. -/// \return The entity value. +/// \brief Creates a text value from doubles. +/// \param data The text data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param data_size The size of the data array or 0 if you wish to clear the data. +/// \return The value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create(char const * value); +IKA_API IkarusTextValue * ikarus_text_value_create(char const ** data, size_t data_size); /// \brief Creates an indeterminate text value. -/// \return The entity value. +/// \return The value. /// \remark Must be freed with #ikarus_free. IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); -/// \brief Fetches the underlying value of a text value. -/// \param value The text value. -/// \return The underlying value. -/// \warning Undefined if the value is indeterminate. -/// \remark The value is owned by libikarus and must not be freed. -IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value); - -/// \brief Sets the value of a text value. +/// \brief Fetches the underlying data of a text value. +/// \details You may adjust the returned data as per your hearts desire. +/// If you need to grow the data, use #ikarus_text_value_set with a new array. +/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. +/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. /// \param value The text value. /// \pre \li Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value); +/// \param data_size_out An out-parameter for the size of the returned array. +/// \remark Ignored if null. +/// \return The underlying data. Owned by LibIkarus, most not be freed. +/// \warning Undefined if the value is indeterminate. +IKA_API char ** ikarus_text_value_get(IkarusTextValue * value, size_t * data_size_out); + +/// \see ikarus_text_value_get +char const * const * ikarus_text_value_get_const(IkarusTextValue const * value, size_t * data_size_out); + +/// \brief Sets the data of a text value. +/// \param value The text value. +/// \pre \li Must not be null. +/// \param new_data The new data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. +IKA_API void ikarus_text_value_set(IkarusTextValue * value, char const ** new_data, size_t new_data_size); /// \brief Converts a text value to an entity value. -/// \param text_value The text value to convert. +/// \param value The text value to convert. /// \return The converted entity value. -IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * text_value); +IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value); IKARUS_END_HEADER diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index fed97d2..89d5678 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -4,42 +4,57 @@ /// \author Folling #include +#include /// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER -/// \brief A true/false boolean-like value. For example "IsDead". +/// \brief A true/false boolean-esque value. For example "Is Dead". struct IkarusToggleValue; -/// \brief Creates a toggle value from a boolean. -/// \param value The toggle value. -/// \return The entity value. +/// \brief Creates a toggle value from doubles. +/// \param data The toggle data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param data_size The size of the data array or 0 if you wish to clear the data. +/// \return The value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value); +IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool * data, size_t data_size); /// \brief Creates an indeterminate toggle value. -/// \return The entity value. +/// \return The value. /// \remark Must be freed with #ikarus_free. IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); -/// \brief Fetches the underlying value of a toggle value. -/// \param value The toggle value. -/// \return The underlying value. -/// \warning Undefined if the value is indeterminate. -IKA_API bool ikarus_toggle_value_get(IkarusToggleValue const * value); - -/// \brief Sets the value of a toggle value. +/// \brief Fetches the underlying data of a toggle value. +/// \details You may adjust the returned data as per your hearts desire. +/// If you need to grow the data, use #ikarus_toggle_value_set with a new array. +/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. +/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. /// \param value The toggle value. /// \pre \li Must not be null. -/// \param new_value The new value. -IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value); +/// \param data_size_out An out-parameter for the size of the returned array. +/// \remark Ignored if null. +/// \return The underlying data. Owned by LibIkarus, most not be freed. +/// \warning Undefined if the value is indeterminate. +IKA_API bool * ikarus_toggle_value_get(IkarusToggleValue * value, size_t * data_size_out); + +/// \see ikarus_toggle_value_get +bool const * ikarus_toggle_value_get_const(IkarusToggleValue const * value, size_t * data_size_out); + +/// \brief Sets the data of a toggle value. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \param new_data The new data or null if you wish to clear the data. +/// \details LibIkarus does not take ownership of this array. +/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. +IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool * new_data, size_t new_data_size); /// \brief Converts a toggle value to an entity value. -/// \param toggle_value The toggle value to convert. +/// \param value The toggle value to convert. /// \return The converted entity value. -IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * toggle_value); +IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value); IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index 1cf1fd0..b894701 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -6,11 +6,14 @@ #include /// \defgroup values Values -/// \brief The values stored in entities. +/// \brief The values of properties. /// \details Each entity has a value for each property it is associated with. -/// The value is of the type specified by the property and constrained by the property's settings. -/// A value may be indeterminate which means it is unknown or not specified. -/// \see PropertyType PropertySettings +/// These value classes represent plain objects. They are not associated with any entity. +/// Each value may be indeterminate. \see IkarusProperty +/// Values are stored as lists. If a property is "singular" then its value is a list of size 1. +/// Values are typed, with types existing for each of the corresponding property types. +/// When setting values for a property the type must match the property type and the value must be valid under the property's +/// settings. \see PropertyType /// @{ IKARUS_BEGIN_HEADER diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index cda8434..d1eb94a 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -8,6 +8,7 @@ #include #include #include +#include IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): IkarusObject{project, id}, @@ -121,6 +122,31 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p } } +IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) { + LOG_VERBOSE("fetching property default value"); + + LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + + auto * ctx = property->get_project()->get_function_context(); + + VTRY( + auto const value, + property->get_project() + ->get_db() + ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->get_id()) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch property's default value: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + return new IkarusValue(property->get_project(), value); +} + void ikarus_property_visit( IkarusProperty * property, void (*toggle_property_visitor)(struct IkarusToggleProperty *, void *), diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp index d2fb82d..70ec934 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/objects/properties/toggle_property.cpp @@ -1,6 +1,10 @@ #include "toggle_property.hpp" IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): - IkarusProperty{project, id, this} { + IkarusProperty{project, id, this} {} + +IkarusToggleProperty * ikarus_toggle_property_create( + struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source +) { } diff --git a/src/persistence/migrations/m1_initial_layout.sql b/src/persistence/migrations/m1_initial_layout.sql index d94fa10..ea24c56 100644 --- a/src/persistence/migrations/m1_initial_layout.sql +++ b/src/persistence/migrations/m1_initial_layout.sql @@ -1,8 +1,9 @@ CREATE TABLE `objects` ( `do_not_access_rowid_alias` INTEGER PRIMARY KEY, - `object_type` INT NOT NULL, - `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56)) VIRTUAL, + `object_type` INT NOT NULL, + `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56) +) VIRTUAL, `name` TEXT NOT NULL, `information` TEXT NOT NULL ) STRICT; @@ -11,7 +12,7 @@ CREATE UNIQUE INDEX `object_id` ON `objects` (`id`); CREATE INDEX `object_type` ON `objects` (`object_type`); CREATE - VIRTUAL TABLE `objects_fts` USING fts5 +VIRTUAL TABLE `objects_fts` USING fts5 ( `name`, `information`, @@ -23,7 +24,7 @@ CREATE "unicode61 remove_diacritics 2 tokenchars '-_'" ); -CREATE TABLE `blueprints` +CREATE TABLE `entities` ( `id` INT, @@ -31,7 +32,7 @@ CREATE TABLE `blueprints` FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; -CREATE TABLE `entities` +CREATE TABLE `blueprints` ( `id` INT, @@ -69,7 +70,7 @@ CREATE INDEX `properties_type` ON `properties` (`type`); CREATE INDEX `properties_source` ON `properties` (`source`); CREATE - VIRTUAL TABLE `property_default_value_fts` USING fts5 +VIRTUAL TABLE `property_default_value_fts` USING fts5 ( `default_value`, content= @@ -81,7 +82,7 @@ CREATE ); CREATE - VIRTUAL TABLE `property_settings_fts` USING fts5 +VIRTUAL TABLE `property_settings_fts` USING fts5 ( `settings`, content= @@ -104,7 +105,7 @@ CREATE TABLE `values` ) WITHOUT ROWID, STRICT; CREATE - VIRTUAL TABLE `values_fts` USING fts5 +VIRTUAL TABLE `values_fts` USING fts5 ( `value`, content= diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp index b4d3413..4a450e3 100644 --- a/src/values/number_value.cpp +++ b/src/values/number_value.cpp @@ -2,25 +2,54 @@ #include -IkarusNumberValue * ikarus_number_value_create(long double value) { - return new IkarusNumberValue{value}; +IkarusNumberValue::IkarusNumberValue(): + IkarusValue{this} {} + +boost::container::vector& IkarusNumberValue::get_value() { + return _value; +} + +boost::container::vector const& IkarusNumberValue::get_value() const { + return _value; +} + +IkarusNumberValue * ikarus_number_value_create(long double * data, size_t data_size) { + auto * ret = new IkarusNumberValue{}; + + ikarus_number_value_set(ret, data, data_size); + + return ret; } IkarusNumberValue * ikarus_number_value_create_indeterminate() { - auto * ret = new IkarusNumberValue{0.0}; + auto * ret = new IkarusNumberValue{}; ret->set_intermediate(true); return ret; } -long double ikarus_number_value_get(IkarusNumberValue const * value) { - return value->get_value(); +long double * ikarus_number_value_get(IkarusNumberValue * value, size_t * data_size_out) { + // NOLINTNEXTLINE(*-pro-type-const-cast) + return const_cast(ikarus_number_value_get_const(value, data_size_out)); } -void ikarus_number_value_set(IkarusNumberValue * value, long double new_value) { - value->set_value(new_value); +long double const * ikarus_number_value_get_const(IkarusNumberValue const * value, size_t * data_size_out) { + if (data_size_out != nullptr) { + *data_size_out = value->get_value().size(); + } + + return value->get_value().data(); } -struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * number_value) { - return static_cast(number_value); +void ikarus_number_value_set(IkarusNumberValue * value, long double * new_data, size_t new_data_size) { + value->get_value().reserve(new_data_size); + + for (auto i = 0; i < new_data_size; ++i) { + // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) + value->get_value().emplace_back(new_data[i]); + } +} + +struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value) { + return static_cast(value); } diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index e495be5..f6befe1 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -1,13 +1,14 @@ #pragma once +#include + +#include + #include -/// \private struct IkarusNumberValue final : IkarusValue { public: - explicit IkarusNumberValue(long double value): - IkarusValue{this}, - _value{value} {} + explicit IkarusNumberValue(); IkarusNumberValue(IkarusNumberValue const&) = default; IkarusNumberValue(IkarusNumberValue&&) = default; @@ -18,14 +19,9 @@ public: ~IkarusNumberValue() override = default; public: - [[nodiscard]] long double get_value() const { - return _value; - } - - void set_value(long double value) { - _value = value; - } + [[nodiscard]] boost::container::vector& get_value(); + [[nodiscard]] boost::container::vector const& get_value() const; private: - long double _value; + boost::container::vector _value{}; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp index 6eb839c..ba30eb8 100644 --- a/src/values/text_value.cpp +++ b/src/values/text_value.cpp @@ -1,26 +1,55 @@ #include "ikarus/values/text_value.h" -#include "text_value.hpp" +#include -IkarusTextValue * ikarus_text_value_create(char const * value) { - return new IkarusTextValue{value}; +IkarusTextValue::IkarusTextValue(): + IkarusValue{this} {} + +boost::container::vector& IkarusTextValue::get_value() { + return _value; +} + +boost::container::vector const& IkarusTextValue::get_value() const { + return _value; +} + +IkarusTextValue * ikarus_text_value_create(char const ** data, size_t data_size) { + auto * ret = new IkarusTextValue{}; + + ikarus_text_value_set(ret, data, data_size); + + return ret; } IkarusTextValue * ikarus_text_value_create_indeterminate() { - auto * ret = new IkarusTextValue{""}; + auto * ret = new IkarusTextValue{}; ret->set_intermediate(true); return ret; } -char const * ikarus_text_value_get(IkarusTextValue const * value) { +char ** ikarus_text_value_get(IkarusTextValue * value, size_t * data_size_out) { + // NOLINTNEXTLINE(*-pro-type-const-cast) + return const_cast(ikarus_text_value_get_const(value, data_size_out)); +} + +char const * const * ikarus_text_value_get_const(IkarusTextValue const * value, size_t * data_size_out) { + if (data_size_out != nullptr) { + *data_size_out = value->get_value().size(); + } + return value->get_value().data(); } -void ikarus_text_value_set(IkarusTextValue * value, char const * new_value) { - value->set_value(new_value); +void ikarus_text_value_set(IkarusTextValue * value, char const ** new_data, size_t new_data_size) { + value->get_value().reserve(new_data_size); + + for (auto i = 0; i < new_data_size; ++i) { + // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) + value->get_value().emplace_back(new_data[i]); + } } -struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * text_value) { - return static_cast(text_value); +struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value) { + return static_cast(value); } diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 3766513..42484a2 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -1,33 +1,28 @@ #pragma once #include +#include + +#include #include -/// \private struct IkarusTextValue final : IkarusValue { public: - explicit IkarusTextValue(std::string value): - IkarusValue{this}, - _value(std::move(value)) {} + explicit IkarusTextValue(); IkarusTextValue(IkarusTextValue const&) = default; - IkarusTextValue(IkarusTextValue&&) noexcept = default; + IkarusTextValue(IkarusTextValue&&) = default; IkarusTextValue& operator=(IkarusTextValue const&) = default; - IkarusTextValue& operator=(IkarusTextValue&&) noexcept = default; + IkarusTextValue& operator=(IkarusTextValue&&) = default; ~IkarusTextValue() override = default; public: - [[nodiscard]] std::string_view get_value() const noexcept { - return _value; - } - - void set_value(std::string_view value) { - _value = value; - } + [[nodiscard]] boost::container::vector& get_value(); + [[nodiscard]] boost::container::vector const& get_value() const; private: - std::string _value; + boost::container::vector _value{}; }; diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp index 30e64c8..c03f71b 100644 --- a/src/values/toggle_value.cpp +++ b/src/values/toggle_value.cpp @@ -1,26 +1,60 @@ #include "ikarus/values/toggle_value.h" -#include "toggle_value.hpp" +#include -IkarusToggleValue * ikarus_toggle_value_create(bool value) { - return new IkarusToggleValue{value}; +IkarusToggleValue::IkarusToggleValue(): + IkarusValue{this} {} + +boost::container::vector& IkarusToggleValue::get_value() { + return _value; +} + +boost::container::vector const& IkarusToggleValue::get_value() const { + return _value; +} + +IkarusToggleValue * ikarus_toggle_value_create(bool * data, size_t data_size) { + auto * ret = new IkarusToggleValue{}; + + ikarus_toggle_value_set(ret, data, data_size); + + return ret; } IkarusToggleValue * ikarus_toggle_value_create_indeterminate() { - auto * ret = new IkarusToggleValue{false}; + auto * ret = new IkarusToggleValue{}; ret->set_intermediate(true); return ret; } -bool ikarus_toggle_value_get(IkarusToggleValue const * value) { - return value->get_value(); +bool * ikarus_toggle_value_get(IkarusToggleValue * value, size_t * data_size_out) { + // NOLINTNEXTLINE(*-pro-type-const-cast) + return const_cast(ikarus_toggle_value_get_const(value, data_size_out)); } -void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value) { - value->set_value(new_value); +bool const * ikarus_toggle_value_get_const(IkarusToggleValue const * value, size_t * data_size_out) { + if (data_size_out != nullptr) { + *data_size_out = value->get_value().size(); + } + + return value->get_value().data(); } -struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * toggle_value) { - return static_cast(toggle_value); +void ikarus_toggle_value_set(IkarusToggleValue * value, bool * new_data, size_t new_data_size) { + if (new_data == nullptr || new_data_size == 0) { + value->get_value().clear(); + return; + } + + value->get_value().reserve(new_data_size); + + for (auto i = 0; i < new_data_size; ++i) { + // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) + value->get_value().emplace_back(new_data[i]); + } +} + +struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value) { + return static_cast(value); } diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index b526476..81386a2 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -1,13 +1,14 @@ #pragma once +#include + +#include + #include -/// \private struct IkarusToggleValue final : IkarusValue { public: - explicit IkarusToggleValue(bool value): - IkarusValue{this}, - _value{value} {} + explicit IkarusToggleValue(); IkarusToggleValue(IkarusToggleValue const&) = default; IkarusToggleValue(IkarusToggleValue&&) = default; @@ -18,14 +19,9 @@ public: ~IkarusToggleValue() override = default; public: - [[nodiscard]] bool get_value() const { - return _value; - } - - void set_value(bool value) { - _value = value; - } + [[nodiscard]] boost::container::vector& get_value(); + [[nodiscard]] boost::container::vector const& get_value() const; private: - bool _value; + boost::container::vector _value{}; }; From 8ad9869d0d94e9c39f0d2982217342a83745bba7 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 3 Dec 2023 10:16:13 +0100 Subject: [PATCH 105/166] adjust values api to vector-esque interface Signed-off-by: Folling --- include/ikarus/values/number_value.h | 61 ++++++++++------- include/ikarus/values/text_value.h | 56 +++++++++------- include/ikarus/values/toggle_value.h | 66 +++++++++++-------- src/values/number_value.cpp | 48 ++++---------- src/values/number_value.hpp | 11 ++-- src/values/text_value.cpp | 48 ++++---------- src/values/text_value.hpp | 12 ++-- src/values/toggle_value.cpp | 53 ++++----------- src/values/toggle_value.hpp | 11 ++-- src/values/value.cpp | 97 +++++++++++++++++++++++++--- src/values/value.hpp | 17 +++-- src/values/value_base.hpp | 35 ++++++++++ vendor/sqlitecpp | 2 +- 13 files changed, 297 insertions(+), 220 deletions(-) create mode 100644 src/values/value_base.hpp diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index 2edb69b..2bdeb85 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -11,14 +11,14 @@ IKARUS_BEGIN_HEADER -/// \brief A true/false long doubleean-esque value. For example "Is Dead". +/// \brief A numeric value. For example "Age" or "Height". struct IkarusNumberValue; -/// \brief Creates a number value from doubles. -/// \param data The number data or null if you wish to clear the data. +/// \brief Creates a number value from long doubles. +/// \param data The number data or null if you wish to create an empty value. /// \details LibIkarus does not take ownership of this array. -/// \param data_size The size of the data array or 0 if you wish to clear the data. -/// \return The value. +/// \param data_size The size of the data array. +/// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. IKA_API IkarusNumberValue * ikarus_number_value_create(long double * data, size_t data_size); @@ -27,29 +27,44 @@ IKA_API IkarusNumberValue * ikarus_number_value_create(long double * data, size_ /// \remark Must be freed with #ikarus_free. IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); -/// \brief Fetches the underlying data of a number value. -/// \details You may adjust the returned data as per your hearts desire. -/// If you need to grow the data, use #ikarus_number_value_set with a new array. -/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. -/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. +/// \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 data_size_out An out-parameter for the size of the returned array. -/// \remark Ignored if null. -/// \return The underlying data. Owned by LibIkarus, most not be freed. -/// \warning Undefined if the value is indeterminate. -IKA_API long double * ikarus_number_value_get(IkarusNumberValue * value, size_t * data_size_out); +/// \param idx The index of the data to fetch. +/// \pre \li Must be less than the size of the value. +/// \return The underlying data or null if an error occurs or the value is indeterminate. +IKA_API long double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx); -/// \see ikarus_number_value_get -long double const * ikarus_number_value_get_const(IkarusNumberValue const * value, size_t * data_size_out); - -/// \brief Sets the data of a number value. +/// \brief Fetches the size of the underlying data of a number value. /// \param value The number value. /// \pre \li Must not be null. -/// \param new_data The new data or null if you wish to clear the data. -/// \details LibIkarus does not take ownership of this array. -/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, long double * new_data, size_t new_data_size); +/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); + +/// \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. +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double new_data); + +/// \brief Removes a data from a number value. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param idx The index of the data to remove. +/// \pre \li Must be less than the size of the value. +/// \remark This will shift all data after the index by one to the left. +IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); + +/// \brief Inserts a data into a number value. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param idx The index of the data to insert. +/// \pre \li Must be less than or equal to the size of the value. +/// \param new_data The new data. +/// \remark This will shift all data after the index by one to the right. +IKA_API void ikarus_number_insert(IkarusNumberValue * value, size_t idx, long double new_data); /// \brief Converts a number value to an entity value. /// \param value The number value to convert. diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index 3f1f682..08a37c8 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -11,14 +11,14 @@ IKARUS_BEGIN_HEADER -/// \brief A true/false char const*ean-esque value. For example "Is Dead". +/// \brief A textual value. For example "Surname" or "Description". struct IkarusTextValue; -/// \brief Creates a text value from doubles. -/// \param data The text data or null if you wish to clear the data. +/// \brief Creates a text value from strings. +/// \param data The text data or null if you wish to create an empty value. /// \details LibIkarus does not take ownership of this array. -/// \param data_size The size of the data array or 0 if you wish to clear the data. -/// \return The value. +/// \param data_size The size of the data array. +/// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. IKA_API IkarusTextValue * ikarus_text_value_create(char const ** data, size_t data_size); @@ -27,29 +27,39 @@ IKA_API IkarusTextValue * ikarus_text_value_create(char const ** data, size_t da /// \remark Must be freed with #ikarus_free. IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); -/// \brief Fetches the underlying data of a text value. -/// \details You may adjust the returned data as per your hearts desire. -/// If you need to grow the data, use #ikarus_text_value_set with a new array. -/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. -/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. +/// \see ikarus_number_value_get +IKA_API char const * const * ikarus_text_value_get(IkarusTextValue const * value, size_t idx); + +/// \brief Fetches the size of the underlying data of a text value. /// \param value The text value. /// \pre \li Must not be null. -/// \param data_size_out An out-parameter for the size of the returned array. -/// \remark Ignored if null. -/// \return The underlying data. Owned by LibIkarus, most not be freed. -/// \warning Undefined if the value is indeterminate. -IKA_API char ** ikarus_text_value_get(IkarusTextValue * value, size_t * data_size_out); +/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value); -/// \see ikarus_text_value_get -char const * const * ikarus_text_value_get_const(IkarusTextValue const * value, size_t * data_size_out); - -/// \brief Sets the data of a text value. +/// \brief Sets the data of a text value at a specific index. /// \param value The text value. /// \pre \li Must not be null. -/// \param new_data The new data or null if you wish to clear the data. -/// \details LibIkarus does not take ownership of this array. -/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. -IKA_API void ikarus_text_value_set(IkarusTextValue * value, char const ** new_data, size_t new_data_size); +/// \param idx The index of the data to set. +/// \pre \li Must be less than the size of the value. +/// \param new_data The new data. +IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data); + +/// \brief Removes a data from a text value. +/// \param value The text value. +/// \pre \li Must not be null. +/// \param idx The index of the data to remove. +/// \pre \li Must be less than the size of the value. +/// \remark This will shift all data after the index by one to the left. +IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx); + +/// \brief Inserts a data into a text value. +/// \param value The text value. +/// \pre \li Must not be null. +/// \param idx The index of the data to insert. +/// \pre \li Must be less than or equal to the size of the value. +/// \param new_data The new data. +/// \remark This will shift all data after the index by one to the right. +IKA_API void ikarus_text_insert(IkarusTextValue * value, size_t idx, char const * new_data); /// \brief Converts a text value to an entity value. /// \param value The text value to convert. diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index 89d5678..2fe45d3 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -14,42 +14,52 @@ IKARUS_BEGIN_HEADER /// \brief A true/false boolean-esque value. For example "Is Dead". struct IkarusToggleValue; -/// \brief Creates a toggle value from doubles. -/// \param data The toggle data or null if you wish to clear the data. -/// \details LibIkarus does not take ownership of this array. -/// \param data_size The size of the data array or 0 if you wish to clear the data. -/// \return The value. +/// \brief Creates a toggle value from booleans. +/// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool * data, size_t data_size); +IKA_API IkarusToggleValue * ikarus_toggle_value_create(); -/// \brief Creates an indeterminate toggle value. -/// \return The value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate(); +/// \brief Fetches the underlying data of a number value at a specific index. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param idx The index of the data to fetch. +/// \pre \li Must be less than the size of the value. +/// \return The underlying data or null if an error occurs or the value is indeterminate. +IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue * value, size_t idx); -/// \brief Fetches the underlying data of a toggle value. -/// \details You may adjust the returned data as per your hearts desire. -/// If you need to grow the data, use #ikarus_toggle_value_set with a new array. -/// Just remember that IkarusValues are plain objects, so changing them won't affect any state. -/// They will need to be submitted to other functions such as #ikarus_entity_set_value to have any effect. +/// \brief Fetches the size of the underlying data of a toggle value. /// \param value The toggle value. /// \pre \li Must not be null. -/// \param data_size_out An out-parameter for the size of the returned array. -/// \remark Ignored if null. -/// \return The underlying data. Owned by LibIkarus, most not be freed. -/// \warning Undefined if the value is indeterminate. -IKA_API bool * ikarus_toggle_value_get(IkarusToggleValue * value, size_t * data_size_out); +/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value); -/// \see ikarus_toggle_value_get -bool const * ikarus_toggle_value_get_const(IkarusToggleValue const * value, size_t * data_size_out); - -/// \brief Sets the data of a toggle value. +/// \brief Sets the data of a toggle value at a specific index. /// \param value The toggle value. /// \pre \li Must not be null. -/// \param new_data The new data or null if you wish to clear the data. -/// \details LibIkarus does not take ownership of this array. -/// \param new_data_size The size of the new data array or 0 if you wish to clear the data. -IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool * new_data, size_t new_data_size); +/// \param idx The index of the data to set. +/// \pre \li Must be less than the size of the value. +/// \param new_data The new data. +IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data); + +/// \brief Removes a data from a toggle value. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \param idx The index of the data to remove. +/// \pre \li Must be less than the size of the value. +IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx); + +/// \brief Inserts a data into a toggle value. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \param idx The index of the data to insert. +/// \pre \li Must be less than or equal to the size of the value. +/// \param new_data The new data. +IKA_API void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data); + +/// \brief Clears a toggle value. +/// \param value The toggle value. +/// \remark Noop if the value is indeterminate. +IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value); /// \brief Converts a toggle value to an entity value. /// \param value The toggle value to convert. diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp index 4a450e3..c928b10 100644 --- a/src/values/number_value.cpp +++ b/src/values/number_value.cpp @@ -1,55 +1,35 @@ #include "ikarus/values/number_value.h" #include +#include IkarusNumberValue::IkarusNumberValue(): IkarusValue{this} {} -boost::container::vector& IkarusNumberValue::get_value() { - return _value; +IkarusNumberValue * ikarus_number_value_create() { + return new IkarusNumberValue{}; } -boost::container::vector const& IkarusNumberValue::get_value() const { - return _value; +long double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx) { + return ikarus_value_base_get(value, idx); } -IkarusNumberValue * ikarus_number_value_create(long double * data, size_t data_size) { - auto * ret = new IkarusNumberValue{}; - - ikarus_number_value_set(ret, data, data_size); - - return ret; +size_t ikarus_number_value_get_size(IkarusNumberValue const * value) { + return ikarus_value_base_get_size(value); } -IkarusNumberValue * ikarus_number_value_create_indeterminate() { - auto * ret = new IkarusNumberValue{}; - ret->set_intermediate(true); - - return ret; +void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double new_data) { + return ikarus_value_base_set(value, idx, new_data); } -long double * ikarus_number_value_get(IkarusNumberValue * value, size_t * data_size_out) { - // NOLINTNEXTLINE(*-pro-type-const-cast) - return const_cast(ikarus_number_value_get_const(value, data_size_out)); +void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx) { + return ikarus_value_base_remove(value, idx); } -long double const * ikarus_number_value_get_const(IkarusNumberValue const * value, size_t * data_size_out) { - if (data_size_out != nullptr) { - *data_size_out = value->get_value().size(); - } - - return value->get_value().data(); -} - -void ikarus_number_value_set(IkarusNumberValue * value, long double * new_data, size_t new_data_size) { - value->get_value().reserve(new_data_size); - - for (auto i = 0; i < new_data_size; ++i) { - // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) - value->get_value().emplace_back(new_data[i]); - } +void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, long double new_data) { + return ikarus_value_base_insert(value, idx, new_data); } struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value) { - return static_cast(value); + return ikarus_value_base_to_value(value); } diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index f6befe1..e1b6915 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -1,12 +1,13 @@ #pragma once -#include - #include #include struct IkarusNumberValue final : IkarusValue { +public: + using data_type = long double; + public: explicit IkarusNumberValue(); @@ -19,9 +20,5 @@ public: ~IkarusNumberValue() override = default; public: - [[nodiscard]] boost::container::vector& get_value(); - [[nodiscard]] boost::container::vector const& get_value() const; - -private: - boost::container::vector _value{}; + boost::container::vector value{}; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp index ba30eb8..a382c0a 100644 --- a/src/values/text_value.cpp +++ b/src/values/text_value.cpp @@ -1,55 +1,35 @@ #include "ikarus/values/text_value.h" #include +#include IkarusTextValue::IkarusTextValue(): IkarusValue{this} {} -boost::container::vector& IkarusTextValue::get_value() { - return _value; +IkarusTextValue * ikarus_text_value_create() { + return new IkarusTextValue{}; } -boost::container::vector const& IkarusTextValue::get_value() const { - return _value; +char const * ikarus_text_value_get(IkarusTextValue * value, size_t idx) { + return ikarus_value_base_get(value, idx)->data(); } -IkarusTextValue * ikarus_text_value_create(char const ** data, size_t data_size) { - auto * ret = new IkarusTextValue{}; - - ikarus_text_value_set(ret, data, data_size); - - return ret; +size_t ikarus_text_value_get_size(IkarusTextValue const * value) { + return ikarus_value_base_get_size(value); } -IkarusTextValue * ikarus_text_value_create_indeterminate() { - auto * ret = new IkarusTextValue{}; - ret->set_intermediate(true); - - return ret; +void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data) { + return ikarus_value_base_set(value, idx, new_data); } -char ** ikarus_text_value_get(IkarusTextValue * value, size_t * data_size_out) { - // NOLINTNEXTLINE(*-pro-type-const-cast) - return const_cast(ikarus_text_value_get_const(value, data_size_out)); +void ikarus_text_value_remove(IkarusTextValue * value, size_t idx) { + return ikarus_value_base_remove(value, idx); } -char const * const * ikarus_text_value_get_const(IkarusTextValue const * value, size_t * data_size_out) { - if (data_size_out != nullptr) { - *data_size_out = value->get_value().size(); - } - - return value->get_value().data(); -} - -void ikarus_text_value_set(IkarusTextValue * value, char const ** new_data, size_t new_data_size) { - value->get_value().reserve(new_data_size); - - for (auto i = 0; i < new_data_size; ++i) { - // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) - value->get_value().emplace_back(new_data[i]); - } +void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data) { + return ikarus_value_base_insert(value, idx, new_data); } struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value) { - return static_cast(value); + return ikarus_value_base_to_value(value); } diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 42484a2..9596a48 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -1,13 +1,13 @@ #pragma once -#include -#include - #include #include struct IkarusTextValue final : IkarusValue { +public: + using data_type = std::string; + public: explicit IkarusTextValue(); @@ -20,9 +20,5 @@ public: ~IkarusTextValue() override = default; public: - [[nodiscard]] boost::container::vector& get_value(); - [[nodiscard]] boost::container::vector const& get_value() const; - -private: - boost::container::vector _value{}; + boost::container::vector value{}; }; diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp index c03f71b..8d11e5a 100644 --- a/src/values/toggle_value.cpp +++ b/src/values/toggle_value.cpp @@ -1,60 +1,35 @@ #include "ikarus/values/toggle_value.h" #include +#include IkarusToggleValue::IkarusToggleValue(): IkarusValue{this} {} -boost::container::vector& IkarusToggleValue::get_value() { - return _value; +IkarusToggleValue * ikarus_toggle_value_create() { + return new IkarusToggleValue{}; } -boost::container::vector const& IkarusToggleValue::get_value() const { - return _value; +bool const * ikarus_toggle_value_get(IkarusToggleValue * value, size_t idx) { + return ikarus_value_base_get(value, idx); } -IkarusToggleValue * ikarus_toggle_value_create(bool * data, size_t data_size) { - auto * ret = new IkarusToggleValue{}; - - ikarus_toggle_value_set(ret, data, data_size); - - return ret; +size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value) { + return ikarus_value_base_get_size(value); } -IkarusToggleValue * ikarus_toggle_value_create_indeterminate() { - auto * ret = new IkarusToggleValue{}; - ret->set_intermediate(true); - - return ret; +void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data) { + return ikarus_value_base_set(value, idx, new_data); } -bool * ikarus_toggle_value_get(IkarusToggleValue * value, size_t * data_size_out) { - // NOLINTNEXTLINE(*-pro-type-const-cast) - return const_cast(ikarus_toggle_value_get_const(value, data_size_out)); +void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx) { + return ikarus_value_base_remove(value, idx); } -bool const * ikarus_toggle_value_get_const(IkarusToggleValue const * value, size_t * data_size_out) { - if (data_size_out != nullptr) { - *data_size_out = value->get_value().size(); - } - - return value->get_value().data(); -} - -void ikarus_toggle_value_set(IkarusToggleValue * value, bool * new_data, size_t new_data_size) { - if (new_data == nullptr || new_data_size == 0) { - value->get_value().clear(); - return; - } - - value->get_value().reserve(new_data_size); - - for (auto i = 0; i < new_data_size; ++i) { - // NOLINTNEXTLINE(*-pro-bounds-pointer-arithmetic) - value->get_value().emplace_back(new_data[i]); - } +void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data) { + return ikarus_value_base_insert(value, idx, new_data); } struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value) { - return static_cast(value); + return ikarus_value_base_to_value(value); } diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index 81386a2..cb8d110 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -1,12 +1,13 @@ #pragma once -#include - #include #include struct IkarusToggleValue final : IkarusValue { +public: + using data_type = bool; + public: explicit IkarusToggleValue(); @@ -19,9 +20,5 @@ public: ~IkarusToggleValue() override = default; public: - [[nodiscard]] boost::container::vector& get_value(); - [[nodiscard]] boost::container::vector const& get_value() const; - -private: - boost::container::vector _value{}; + boost::container::vector value{}; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index 7528ec8..e0e9355 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -1,9 +1,13 @@ #include "ikarus/values/value.h" +#include + #include #include +#include + #include #include #include @@ -12,11 +16,90 @@ IkarusValue::IkarusValue(Data data): _data(data) {} -bool IkarusValue::is_interminate() const { +cppbase::Result IkarusValue::from_json(boost::json::value const& json) { + bool const * intermediate = nullptr; + boost::int64_t const * type = nullptr; + boost::json::array const * data = nullptr; + + if (auto const * obj = json.if_object(); obj == nullptr) { + return cppbase::err(FromJsonError{}); + } else { + if (auto const * type_value = obj->if_contains("type"); type_value == nullptr) { + return cppbase::err(FromJsonError{}); + } else if (type = type_value->if_int64(); type == nullptr) { + return cppbase::err(FromJsonError{}); + } + + if (auto const * intermediate_value = obj->if_contains("intermediate"); intermediate_value == nullptr) { + return cppbase::err(FromJsonError{}); + } else if (intermediate = intermediate_value->if_bool(); intermediate == nullptr) { + return cppbase::err(FromJsonError{}); + } + + if (auto const * data = obj->if_contains("data"); data == nullptr) { + return cppbase::err(FromJsonError{}); + } else if (auto * array = data->if_array(); array == nullptr) { + return cppbase::err(FromJsonError{}); + } + + auto assign_value = + []( + auto const& value, boost::json::array const& array, auto convert = [](auto const& val) { return val; } + ) { + for (auto const& data : array) { + if (auto res = boost::json::try_value_to(data); res.has_error()) { + return cppbase::err(FromJsonError{}); + } else { + value->get_data().push_back(convert(res.value())); + return cppbase::ok(); + } + } + }; + + switch (*type) { + case IkarusPropertyType_Toggle: { + auto * ret = new IkarusToggleValue{}; + ret->set_intermediate(*intermediate); + + assign_value.operator()(ret, *data); + } + case IkarusPropertyType_Number: { + auto * ret = new IkarusToggleValue{}; + ret->set_intermediate(*intermediate); + + assign_value.operator()(ret, *data); + } + case IkarusPropertyType_Text: { + auto * ret = new IkarusToggleValue{}; + ret->set_intermediate(*intermediate); + + assign_value.operator()(ret, *data); + } + default: return cppbase::err(FromJsonError{}); + } + } +} + +boost::json::value IkarusValue::to_json() const { + return { + {"indeterminate", _indeterminate}, + {"type", + std::visit( + cppbase::overloaded{ + []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusPropertyType_Toggle; }, + []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusPropertyType_Number; }, + []([[maybe_unused]] IkarusTextValue const * value) { return IkarusPropertyType_Text; }, + }, _data + )}, + {"data", std::visit([](auto const * value) { return boost::json::value_from(value->get_data()); }, _data)} + }; +} + +bool IkarusValue::is_indeterminate() const { return _indeterminate; } -void IkarusValue::set_intermediate(bool value) { +void IkarusValue::set_indeterminate(bool value) { _indeterminate = value; } @@ -29,7 +112,7 @@ IkarusValue::Data const& IkarusValue::get_data() const { } bool ikarus_value_is_indeterminate(IkarusValue const * value) { - return value->is_interminate(); + return value->is_indeterminate(); } void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { @@ -39,18 +122,14 @@ void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { char const * ikarus_value_to_string(IkarusValue const * value) { auto const str = std::visit( cppbase::overloaded{ - [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->get_value() ? "true" : "false"; }, + [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->get_data() ? "true" : "false"; }, [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->get_value()); }, [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->get_value()); }, }, value->get_data() ); - auto * const ret = new char[str.size() + 1]; - - std::strncpy(ret, str.data(), str.size() + 1); - - return ret; + return strdup(str.data()); } void ikarus_value_visit( diff --git a/src/values/value.hpp b/src/values/value.hpp index ddf6601..c58d9c7 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -2,6 +2,10 @@ #include +#include + +#include + struct IkarusValue { public: using Data = std::variant; @@ -18,13 +22,12 @@ public: virtual ~IkarusValue() = default; public: - [[nodiscard]] bool is_interminate() const; - void set_intermediate(bool value); + struct FromJsonError {}; - [[nodiscard]] Data& get_data(); - [[nodiscard]] Data const& get_data() const; + [[nodiscard]] static cppbase::Result from_json(boost::json::value const& json); + [[nodiscard]] boost::json::value to_json() const; -private: - bool _indeterminate{false}; - Data _data; +public: + bool indeterminate{false}; + Data data; }; diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp new file mode 100644 index 0000000..7fbb589 --- /dev/null +++ b/src/values/value_base.hpp @@ -0,0 +1,35 @@ +#pragma once + +template +typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { + if (value->is_indeterminate()) { + return nullptr; + } + + return &value->value[idx]; +} + +template +size_t ikarus_value_base_get_size(V const * value) { + return value->value.size(); +} + +template +void ikarus_value_base_set(V * value, size_t idx, typename V::data_type new_data) { + value->value[idx] = new_data; +} + +template +void ikarus_value_base_remove(V * value, size_t idx) { + value->value.erase(value->value.begin() + idx); +} + +template +void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_data) { + value->value.insert(value->value.begin() + idx, new_data); +} + +template +struct IkarusValue * ikarus_value_base_to_value(V * value) { + return static_cast(value); +} diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 07b806f..6e39dd2 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 07b806f4d73b27a555a3ba82a97d89913e430e48 +Subproject commit 6e39dd241f8469784b8a356e7a9563fa58b0e2c4 From 2f5159beac75b0e98fb18733bd9aa5c50c29df4a Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 3 Dec 2023 11:41:06 +0100 Subject: [PATCH 106/166] implement value.hpp Signed-off-by: Folling --- src/values/number_value.hpp | 3 +- src/values/text_value.hpp | 2 +- src/values/toggle_value.hpp | 2 +- src/values/value.cpp | 152 +++++++++++++++++------------------- src/values/value.hpp | 6 +- src/values/value_base.hpp | 24 ++++-- 6 files changed, 93 insertions(+), 96 deletions(-) diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index e1b6915..359e5d2 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -20,5 +21,5 @@ public: ~IkarusNumberValue() override = default; public: - boost::container::vector value{}; + boost::variant2::variant> data{}; }; diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 9596a48..191206e 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -20,5 +20,5 @@ public: ~IkarusTextValue() override = default; public: - boost::container::vector value{}; + boost::variant2::variant> data{}; }; diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index cb8d110..750af25 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -20,5 +20,5 @@ public: ~IkarusToggleValue() override = default; public: - boost::container::vector value{}; + boost::variant2::variant> data{}; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index e0e9355..dd1b60e 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -2,9 +2,10 @@ #include -#include +#include +#include -#include +#include #include @@ -14,12 +15,11 @@ #include IkarusValue::IkarusValue(Data data): - _data(data) {} + data(data) {} cppbase::Result IkarusValue::from_json(boost::json::value const& json) { - bool const * intermediate = nullptr; boost::int64_t const * type = nullptr; - boost::json::array const * data = nullptr; + boost::json::value const * data = nullptr; if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); @@ -30,103 +30,91 @@ cppbase::Result IkarusValue::from_json( return cppbase::err(FromJsonError{}); } - if (auto const * intermediate_value = obj->if_contains("intermediate"); intermediate_value == nullptr) { - return cppbase::err(FromJsonError{}); - } else if (intermediate = intermediate_value->if_bool(); intermediate == nullptr) { + if (data = obj->if_contains("data"); data == nullptr) { return cppbase::err(FromJsonError{}); } - if (auto const * data = obj->if_contains("data"); data == nullptr) { - return cppbase::err(FromJsonError{}); - } else if (auto * array = data->if_array(); array == nullptr) { - return cppbase::err(FromJsonError{}); - } - - auto assign_value = - []( - auto const& value, boost::json::array const& array, auto convert = [](auto const& val) { return val; } - ) { - for (auto const& data : array) { - if (auto res = boost::json::try_value_to(data); res.has_error()) { - return cppbase::err(FromJsonError{}); - } else { - value->get_data().push_back(convert(res.value())); - return cppbase::ok(); - } - } - }; - switch (*type) { case IkarusPropertyType_Toggle: { auto * ret = new IkarusToggleValue{}; - ret->set_intermediate(*intermediate); + auto res = boost::json::try_value_to>(*data); - assign_value.operator()(ret, *data); + if (res.has_error()) { + return cppbase::err(FromJsonError{}); + } + + ret->data = std::move(res.value()); } case IkarusPropertyType_Number: { - auto * ret = new IkarusToggleValue{}; - ret->set_intermediate(*intermediate); + auto * ret = new IkarusNumberValue{}; + auto res = boost::json::try_value_to>(*data); - assign_value.operator()(ret, *data); + if (res.has_error()) { + return cppbase::err(FromJsonError{}); + } + + ret->data = std::move(res.value()); } case IkarusPropertyType_Text: { - auto * ret = new IkarusToggleValue{}; - ret->set_intermediate(*intermediate); + auto * ret = new IkarusTextValue{}; + auto res = boost::json::try_value_to>(*data); - assign_value.operator()(ret, *data); + if (res.has_error()) { + return cppbase::err(FromJsonError{}); + } + + ret->data = std::move(res.value()); + } + default: { + return cppbase::err(FromJsonError{}); } - default: return cppbase::err(FromJsonError{}); } } } boost::json::value IkarusValue::to_json() const { + auto type = boost::variant2::visit( + boost::make_overloaded_function( + []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusPropertyType_Toggle; }, + []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusPropertyType_Number; }, + []([[maybe_unused]] IkarusTextValue const * value) { return IkarusPropertyType_Text; } + ), + data + ); + + auto data_json = boost::variant2::visit([](auto const * value) { return boost::json::value_from(value->data); }, data); + return { - {"indeterminate", _indeterminate}, - {"type", - std::visit( - cppbase::overloaded{ - []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusPropertyType_Toggle; }, - []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusPropertyType_Number; }, - []([[maybe_unused]] IkarusTextValue const * value) { return IkarusPropertyType_Text; }, - }, _data - )}, - {"data", std::visit([](auto const * value) { return boost::json::value_from(value->get_data()); }, _data)} + {"type", type}, + {"data", data_json} }; } -bool IkarusValue::is_indeterminate() const { - return _indeterminate; -} - -void IkarusValue::set_indeterminate(bool value) { - _indeterminate = value; -} - -IkarusValue::Data& IkarusValue::get_data() { - return _data; -} - -IkarusValue::Data const& IkarusValue::get_data() const { - return _data; -} - bool ikarus_value_is_indeterminate(IkarusValue const * value) { - return value->is_indeterminate(); + return boost::variant2::visit( + [](auto const * value) { return boost::variant2::holds_alternative(value->data); }, + value->data + ); } void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { - value->set_intermediate(indeterminate); + if (indeterminate) { + return boost::variant2::visit([](auto const * value) { value->data = boost::variant2::monostate{}; }, value->data); + } + + return boost::variant2::visit( + [](auto const * value) { value->data = boost::remove_pointer_t::data_type{}; }, value->data + ); } char const * ikarus_value_to_string(IkarusValue const * value) { - auto const str = std::visit( - cppbase::overloaded{ - [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->get_data() ? "true" : "false"; }, - [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->get_value()); }, - [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->get_value()); }, - }, - value->get_data() + auto const str = boost::variant2::visit( + boost::make_overloaded_function( + [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->data ? "true" : "false"; }, + [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->data); }, + [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->data); } + ), + value->data ); return strdup(str.data()); @@ -139,13 +127,13 @@ void ikarus_value_visit( void (*text_visitor)(IkarusTextValue *, void *), void * data ) { - std::visit( - cppbase::overloaded{ + boost::variant2::visit( + boost::make_overloaded_function( [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); }, - }, - value->get_data() + [text_visitor, data](IkarusTextValue * text_value) { text_visitor(text_value, data); } + ), + value->data ); } @@ -156,12 +144,12 @@ void ikarus_value_visit_const( void (*text_visitor)(IkarusTextValue const *, void *), void * data ) { - std::visit( - cppbase::overloaded{ + boost::variant2::visit( + boost::make_overloaded_function( [toggle_visitor, data](IkarusToggleValue const * toggle_value) { toggle_visitor(toggle_value, data); }, [number_visitor, data](IkarusNumberValue const * number_value) { number_visitor(number_value, data); }, - [text_visitor, data](IkarusTextValue const * text_value) { text_visitor(text_value, data); }, - }, - value->get_data() + [text_visitor, data](IkarusTextValue const * text_value) { text_visitor(text_value, data); } + ), + value->data ); } diff --git a/src/values/value.hpp b/src/values/value.hpp index c58d9c7..3fbfad9 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -1,14 +1,13 @@ #pragma once -#include - #include +#include #include struct IkarusValue { public: - using Data = std::variant; + using Data = boost::variant2::variant; public: explicit IkarusValue(Data data); @@ -28,6 +27,5 @@ public: [[nodiscard]] boost::json::value to_json() const; public: - bool indeterminate{false}; Data data; }; diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp index 7fbb589..bad1b91 100644 --- a/src/values/value_base.hpp +++ b/src/values/value_base.hpp @@ -2,31 +2,41 @@ template typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { - if (value->is_indeterminate()) { - return nullptr; + if (auto * data = value->data.template get_if>(); data != nullptr) { + return &(*data)[idx]; } - return &value->value[idx]; + return nullptr; } template size_t ikarus_value_base_get_size(V const * value) { - return value->value.size(); + if (auto * data = value->data.template get_if>(); data != nullptr) { + return data->size(); + } + + return 0; } template void ikarus_value_base_set(V * value, size_t idx, typename V::data_type new_data) { - value->value[idx] = new_data; + if (auto * data = value->data.template get_if>(); data != nullptr) { + (*data)[idx] = new_data; + } } template void ikarus_value_base_remove(V * value, size_t idx) { - value->value.erase(value->value.begin() + idx); + if (auto * data = value->data.template get_if>(); data != nullptr) { + data->erase(data->begin() + idx); + } } template void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_data) { - value->value.insert(value->value.begin() + idx, new_data); + if (auto * data = value->data.template get_if>(); data != nullptr) { + data->insert(data->begin() + idx, new_data); + } } template From a934564afc99f8bddedde02bf61f37bd304f100c Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 3 Dec 2023 18:44:09 +0100 Subject: [PATCH 107/166] move sub-functions of value impls to concrete types Signed-off-by: Folling --- include/ikarus/objects/properties/property.h | 2 +- include/ikarus/values/number_value.h | 70 +++++++++++++---- include/ikarus/values/text_value.h | 80 +++++++++++++++----- include/ikarus/values/toggle_value.h | 51 ++++++++++++- include/ikarus/values/value.h | 21 +---- src/values/number_value.cpp | 35 +++++++++ src/values/number_value.hpp | 4 +- src/values/text_value.cpp | 35 +++++++++ src/values/text_value.hpp | 3 +- src/values/toggle_value.cpp | 41 ++++++++++ src/values/toggle_value.hpp | 3 +- src/values/value.cpp | 76 +++++-------------- src/values/value.hpp | 1 + src/values/value_base.hpp | 36 +++++++++ 14 files changed, 339 insertions(+), 119 deletions(-) diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index 7aa6a2a..c0f37fa 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -84,7 +84,7 @@ IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusPro /// \pre \li Must exist. /// \return The default value of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property); +IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusProperty const * property); /// \brief Visits a property. Calling the appropriate function for the property's type. /// \param property The property to visit. diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index 2bdeb85..7049af8 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -14,31 +14,23 @@ IKARUS_BEGIN_HEADER /// \brief A numeric value. For example "Age" or "Height". struct IkarusNumberValue; -/// \brief Creates a number value from long doubles. -/// \param data The number data or null if you wish to create an empty value. -/// \details LibIkarus does not take ownership of this array. -/// \param data_size The size of the data array. +/// \brief Creates a number value. /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create(long double * data, size_t data_size); - -/// \brief Creates an indeterminate number value. -/// \return The value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate(); +IKA_API IkarusNumberValue * ikarus_number_value_create(); /// \brief Fetches the underlying data of a number value at a specific index. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. -/// \return The underlying data or null if an error occurs or the value is indeterminate. +/// \return The underlying data or null if an error occurs or the value is undefined. IKA_API long double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx); /// \brief Fetches the size of the underlying data of a number value. /// \param value The number value. /// \pre \li Must not be null. -/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +/// \return The size of the underlying data or 0 if an error occurs or the value is undefined. IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); /// \brief Sets the data of a number value at a specific index. @@ -47,14 +39,13 @@ IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); /// \param idx The index of the data to set. /// \pre \li Must be less than the size of the value. /// \param new_data The new data. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double new_data); +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double const * new_data); /// \brief Removes a data from a number value. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to remove. /// \pre \li Must be less than the size of the value. -/// \remark This will shift all data after the index by one to the left. IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); /// \brief Inserts a data into a number value. @@ -63,14 +54,61 @@ IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. /// \param new_data The new data. -/// \remark This will shift all data after the index by one to the right. -IKA_API void ikarus_number_insert(IkarusNumberValue * value, size_t idx, long double new_data); +IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, long double const * new_data); + +/// \brief Clears a number value. +/// \param value The number value. +/// \remark Noop if the value is undefined. +IKA_API void ikarus_number_value_clear(IkarusNumberValue * value); + +/// \brief Checks if a number value is undefined. +/// \param value The number value. +/// \pre \li Must not be null. +/// \return True if the value is undefined, false otherwise. +IKA_API bool ikarus_number_value_is_undefined(IkarusNumberValue const * value); + +/// \brief Changes a number value's undefined state. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param undefined The new undefined state. +/// \remark Noop if the value is already undefined. +/// \remark If the value is set to undefined, all data will be cleared. +/// \remark If the value is set to not undefined, the value is as if newly created. +IKA_API void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined); + +/// \brief Converts a number value to a string. +/// \param value The number value to convert. +/// \pre \li Must not be null. +/// \return The converted string. +/// \remark Must be freed with #ikarus_free. +/// \remark Undefined if the value is undefined. +IKA_API char const * ikarus_number_value_to_string(IkarusNumberValue const * value); + +/// \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. +/// \return True if the values' data are equal, false otherwise. +IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs); + +/// \brief Creates a copy of a number value. +/// \param value The value to copy. +/// \pre \li Must not be null. +/// \return The copied value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value); /// \brief Converts a number value to an entity value. /// \param value The number value to convert. +/// \pre \li Must not be null. /// \return The converted entity value. +/// \remark This is the same pointer, so freeing it implies freeing the original value. IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value); +/// \see ikarus_toggle_value_to_value +IKA_API struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index 08a37c8..b40a7f6 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -11,29 +11,27 @@ IKARUS_BEGIN_HEADER -/// \brief A textual value. For example "Surname" or "Description". +/// \brief A textual value. For example "Surname" or "Description" struct IkarusTextValue; -/// \brief Creates a text value from strings. -/// \param data The text data or null if you wish to create an empty value. -/// \details LibIkarus does not take ownership of this array. -/// \param data_size The size of the data array. +/// \brief Creates a text value. /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create(char const ** data, size_t data_size); +IKA_API IkarusTextValue * ikarus_text_value_create(); -/// \brief Creates an indeterminate text value. -/// \return The value. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate(); - -/// \see ikarus_number_value_get -IKA_API char const * const * ikarus_text_value_get(IkarusTextValue const * value, size_t idx); +/// \brief Fetches the underlying data of a number value at a specific index. +/// \param value The number value. +/// \pre \li Must not be null. +/// \param idx The index of the data to fetch. +/// \pre \li Must be less than the size of the value. +/// \return The underlying data or null if an error occurs or the value is undefined. +/// \remark This value is owned by LibIkarus and must not be freed. +IKA_API char const * ikarus_text_value_get(IkarusTextValue * value, size_t idx); /// \brief Fetches the size of the underlying data of a text value. /// \param value The text value. /// \pre \li Must not be null. -/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +/// \return The size of the underlying data or 0 if an error occurs or the value is undefined. IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value); /// \brief Sets the data of a text value at a specific index. @@ -41,7 +39,7 @@ IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * 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 new_data The new data. LibIkarus assumes ownership of this pointer. IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data); /// \brief Removes a data from a text value. @@ -49,7 +47,6 @@ IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char con /// \pre \li Must not be null. /// \param idx The index of the data to remove. /// \pre \li Must be less than the size of the value. -/// \remark This will shift all data after the index by one to the left. IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx); /// \brief Inserts a data into a text value. @@ -58,14 +55,61 @@ IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx); /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. /// \param new_data The new data. -/// \remark This will shift all data after the index by one to the right. -IKA_API void ikarus_text_insert(IkarusTextValue * value, size_t idx, char const * new_data); +IKA_API void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data); + +/// \brief Clears a text value. +/// \param value The text value. +/// \remark Noop if the value is undefined. +IKA_API void ikarus_text_value_clear(IkarusTextValue * value); + +/// \brief Checks if a text value is undefined. +/// \param value The text value. +/// \pre \li Must not be null. +/// \return True if the value is undefined, false otherwise. +IKA_API bool ikarus_text_value_is_undefined(IkarusTextValue const * value); + +/// \brief Changes a text value's undefined state. +/// \param value The text value. +/// \pre \li Must not be null. +/// \param undefined The new undefined state. +/// \remark Noop if the value is already undefined. +/// \remark If the value is set to undefined, all data will be cleared. +/// \remark If the value is set to not undefined, the value is as if newly created. +IKA_API void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined); + +/// \brief Converts a text value to a string. +/// \param value The text value to convert. +/// \pre \li Must not be null. +/// \return The converted string. +/// \remark Must be freed with #ikarus_free. +/// \remark Undefined if the value is undefined. +IKA_API char const * ikarus_text_value_to_string(IkarusTextValue const * value); + +/// \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. +/// \return True if the values' data are equal, false otherwise. +IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs); + +/// \brief Creates a copy of a text value. +/// \param value The value to copy. +/// \pre \li Must not be null. +/// \return The copied value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value); /// \brief Converts a text value to an entity value. /// \param value The text value to convert. +/// \pre \li Must not be null. /// \return The converted entity value. +/// \remark This is the same pointer, so freeing it implies freeing the original value. IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value); +/// \see ikarus_toggle_value_to_value +IKA_API struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index 2fe45d3..2bb84c7 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -14,7 +14,7 @@ IKARUS_BEGIN_HEADER /// \brief A true/false boolean-esque value. For example "Is Dead". struct IkarusToggleValue; -/// \brief Creates a toggle value from booleans. +/// \brief Creates a toggle. /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. IKA_API IkarusToggleValue * ikarus_toggle_value_create(); @@ -24,13 +24,13 @@ IKA_API IkarusToggleValue * ikarus_toggle_value_create(); /// \pre \li Must not be null. /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. -/// \return The underlying data or null if an error occurs or the value is indeterminate. +/// \return The underlying data or null if an error occurs or the value is undefined. IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue * value, size_t idx); /// \brief Fetches the size of the underlying data of a toggle value. /// \param value The toggle value. /// \pre \li Must not be null. -/// \return The size of the underlying data or 0 if an error occurs or the value is indeterminate. +/// \return The size of the underlying data or 0 if an error occurs or the value is undefined. IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value); /// \brief Sets the data of a toggle value at a specific index. @@ -58,14 +58,57 @@ IKA_API void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, b /// \brief Clears a toggle value. /// \param value The toggle value. -/// \remark Noop if the value is indeterminate. +/// \remark Noop if the value is undefined. IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value); +/// \brief Checks if a toggle value is undefined. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \return True if the value is undefined, false otherwise. +IKA_API bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value); + +/// \brief Changes a toggle value's undefined state. +/// \param value The toggle value. +/// \pre \li Must not be null. +/// \param undefined The new undefined state. +/// \remark Noop if the value is already undefined. +/// \remark If the value is set to undefined, all data will be cleared. +/// \remark If the value is set to not undefined, the value is as if newly created. +IKA_API void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined); + +/// \brief Converts a toggle value to a string. +/// \param value The toggle value to convert. +/// \pre \li Must not be null. +/// \return The converted string. +/// \remark Must be freed with #ikarus_free. +/// \remark Undefined if the value is undefined. +IKA_API char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value); + +/// \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. +/// \return True if the values' data are equal, false otherwise. +IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs); + +/// \brief Creates a copy of a toggle value. +/// \param value The value to copy. +/// \pre \li Must not be null. +/// \return The copied value. +/// \remark Must be freed with #ikarus_free. +IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value); + /// \brief Converts a toggle value to an entity value. /// \param value The toggle value to convert. +/// \pre \li Must not be null. /// \return The converted entity value. +/// \remark This is the same pointer, so freeing it implies freeing the original value. IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value); +/// \see ikarus_toggle_value_to_value +IKA_API struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index b894701..d33f87a 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -9,7 +9,7 @@ /// \brief The values of properties. /// \details Each entity has a value for each property it is associated with. /// These value classes represent plain objects. They are not associated with any entity. -/// Each value may be indeterminate. \see IkarusProperty +/// Each value may be undefined. \see IkarusProperty /// Values are stored as lists. If a property is "singular" then its value is a list of size 1. /// Values are typed, with types existing for each of the corresponding property types. /// When setting values for a property the type must match the property type and the value must be valid under the property's @@ -21,25 +21,6 @@ IKARUS_BEGIN_HEADER /// \brief The common type for all values. struct IkarusValue; -/// \brief Checks if a value is indeterminate. -/// \param value The value. -/// \pre \li Must not be null. -/// \return True if the value is indeterminate, false otherwise. -IKA_API bool ikarus_value_is_indeterminate(IkarusValue const * value); - -/// \brief Sets the indeterminate state of a value. -/// \param value The value. -/// \pre \li Must not be null. -/// \param indeterminate The new indeterminate state. -IKA_API void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate); - -/// \brief Converts an entity value to a string. -/// \pre \li Must not be null. -/// \param value The entity value. -/// \return A string representation of the value or null if an error occurred. -/// \remark The returned value is owned by the caller. -IKA_API char const * ikarus_value_to_string(IkarusValue const * value); - /// \brief Visits an entity value, calling the appropriate function for the value's type. /// \param value The entity value to visit. /// \pre \li Must not be null. diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp index c928b10..cef3508 100644 --- a/src/values/number_value.cpp +++ b/src/values/number_value.cpp @@ -1,5 +1,7 @@ #include "ikarus/values/number_value.h" +#include + #include #include @@ -30,6 +32,39 @@ void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, long doub return ikarus_value_base_insert(value, idx, new_data); } +void ikarus_number_value_clear(IkarusNumberValue * value) { + return ikarus_value_base_clear(value); +} + +bool ikarus_number_value_is_undefined(IkarusNumberValue const * value) { + return ikarus_value_base_is_undefined(value); +} + +void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined) { + return ikarus_value_base_set_undefined(value, undefined); +} + +char const * ikarus_number_value_to_string(IkarusNumberValue const * value) { + return boost::variant2::visit( + boost::make_overloaded_function( + [](boost::variant2::monostate const&) { return nullptr; }, [](auto const& data) { return fmt::join(data, ", "); } + ), + value->data + ); +} + +bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs) { + return ikarus_value_base_is_equal(lhs, rhs); +} + +IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value) { + return ikarus_value_base_copy(value); +} + struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value) { return ikarus_value_base_to_value(value); } + +struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value) { + return ikarus_value_base_to_value_const(value); +} diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index 359e5d2..6cad130 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -21,5 +21,7 @@ public: ~IkarusNumberValue() override = default; public: - boost::variant2::variant> data{}; + boost::variant2:: + variant> + data{}; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp index a382c0a..08ad03a 100644 --- a/src/values/text_value.cpp +++ b/src/values/text_value.cpp @@ -1,5 +1,7 @@ #include "ikarus/values/text_value.h" +#include + #include #include @@ -30,6 +32,39 @@ void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * return ikarus_value_base_insert(value, idx, new_data); } +void ikarus_text_value_clear(IkarusTextValue * value) { + return ikarus_value_base_clear(value); +} + +bool ikarus_text_value_is_undefined(IkarusTextValue const * value) { + return ikarus_value_base_is_undefined(value); +} + +void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined) { + return ikarus_value_base_set_undefined(value, undefined); +} + +char const * ikarus_text_value_to_string(IkarusTextValue const * value) { + return boost::variant2::visit( + boost::make_overloaded_function( + [](boost::variant2::monostate const&) { return nullptr; }, [](auto const& data) { return fmt::join(data, ", "); } + ), + value->data + ); +} + +bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs) { + return ikarus_value_base_is_equal(lhs, rhs); +} + +IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value) { + return ikarus_value_base_copy(value); +} + struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value) { return ikarus_value_base_to_value(value); } + +struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value) { + return ikarus_value_base_to_value_const(value); +} diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 191206e..8d063f2 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -20,5 +20,6 @@ public: ~IkarusTextValue() override = default; public: - boost::variant2::variant> data{}; + boost::variant2::variant> data{ + }; }; diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp index 8d11e5a..37b2069 100644 --- a/src/values/toggle_value.cpp +++ b/src/values/toggle_value.cpp @@ -1,5 +1,8 @@ #include "ikarus/values/toggle_value.h" +#include +#include + #include #include @@ -30,6 +33,44 @@ void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_ return ikarus_value_base_insert(value, idx, new_data); } +void ikarus_toggle_value_clear(IkarusToggleValue * value) { + return ikarus_value_base_clear(value); +} + +bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value) { + return ikarus_value_base_is_undefined(value); +} + +void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined) { + return ikarus_value_base_set_undefined(value, undefined); +} + +char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value) { + return boost::variant2::visit( + boost::make_overloaded_function( + [](boost::variant2::monostate const&) { return nullptr; }, + [](auto const& data) { + return fmt::join( + data | boost::adaptors::transformed([](auto const& bool_value) { return bool_value ? "✓" : "✗"; }), ", " + ); + } + ), + value->data + ); +} + +bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs) { + return ikarus_value_base_is_equal(lhs, rhs); +} + +IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value) { + return ikarus_value_base_copy(value); +} + struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value) { return ikarus_value_base_to_value(value); } + +struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value) { + return ikarus_value_base_to_value_const(value); +} diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index 750af25..6506dda 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -20,5 +20,6 @@ public: ~IkarusToggleValue() override = default; public: - boost::variant2::variant> data{}; + boost::variant2::variant> data{ + }; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index dd1b60e..b9a53a7 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -18,12 +18,12 @@ IkarusValue::IkarusValue(Data data): data(data) {} cppbase::Result IkarusValue::from_json(boost::json::value const& json) { - boost::int64_t const * type = nullptr; - boost::json::value const * data = nullptr; - if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); } else { + boost::int64_t const * type = nullptr; + boost::json::value const * data = nullptr; + if (auto const * type_value = obj->if_contains("type"); type_value == nullptr) { return cppbase::err(FromJsonError{}); } else if (type = type_value->if_int64(); type == nullptr) { @@ -34,36 +34,28 @@ cppbase::Result IkarusValue::from_json( return cppbase::err(FromJsonError{}); } + auto create_value = [data]() -> cppbase::Result { + auto * ret = new T{}; + auto res = boost::json::try_value_to>(*data); + + if (res.has_error()) { + return cppbase::err(FromJsonError{}); + } + + ret->data = std::move(res.value()); + + return cppbase::ok(ret); + }; + switch (*type) { case IkarusPropertyType_Toggle: { - auto * ret = new IkarusToggleValue{}; - auto res = boost::json::try_value_to>(*data); - - if (res.has_error()) { - return cppbase::err(FromJsonError{}); - } - - ret->data = std::move(res.value()); + return create_value.operator()(); } case IkarusPropertyType_Number: { - auto * ret = new IkarusNumberValue{}; - auto res = boost::json::try_value_to>(*data); - - if (res.has_error()) { - return cppbase::err(FromJsonError{}); - } - - ret->data = std::move(res.value()); + return create_value.operator()(); } case IkarusPropertyType_Text: { - auto * ret = new IkarusTextValue{}; - auto res = boost::json::try_value_to>(*data); - - if (res.has_error()) { - return cppbase::err(FromJsonError{}); - } - - ret->data = std::move(res.value()); + return create_value.operator()(); } default: { return cppbase::err(FromJsonError{}); @@ -90,36 +82,6 @@ boost::json::value IkarusValue::to_json() const { }; } -bool ikarus_value_is_indeterminate(IkarusValue const * value) { - return boost::variant2::visit( - [](auto const * value) { return boost::variant2::holds_alternative(value->data); }, - value->data - ); -} - -void ikarus_value_set_indeterminate(IkarusValue * value, bool indeterminate) { - if (indeterminate) { - return boost::variant2::visit([](auto const * value) { value->data = boost::variant2::monostate{}; }, value->data); - } - - return boost::variant2::visit( - [](auto const * value) { value->data = boost::remove_pointer_t::data_type{}; }, value->data - ); -} - -char const * ikarus_value_to_string(IkarusValue const * value) { - auto const str = boost::variant2::visit( - boost::make_overloaded_function( - [](IkarusToggleValue const * toggle_value) -> std::string { return toggle_value->data ? "true" : "false"; }, - [](IkarusNumberValue const * number_value) -> std::string { return fmt::format("{}", number_value->data); }, - [](IkarusTextValue const * text_value) -> std::string { return fmt::format("{}", text_value->data); } - ), - value->data - ); - - return strdup(str.data()); -} - void ikarus_value_visit( IkarusValue * value, void (*toggle_visitor)(IkarusToggleValue *, void *), diff --git a/src/values/value.hpp b/src/values/value.hpp index 3fbfad9..14c2a73 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -8,6 +8,7 @@ struct IkarusValue { public: using Data = boost::variant2::variant; + constexpr auto SMALL_VEC_VALUE_SIZE = 8; public: explicit IkarusValue(Data data); diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp index bad1b91..04bad48 100644 --- a/src/values/value_base.hpp +++ b/src/values/value_base.hpp @@ -39,7 +39,43 @@ void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_d } } +template +void ikarus_value_base_clear(V * value) { + if (auto * data = value->data.template get_if>(); data != nullptr) { + data->clear(); + } +} + +template +bool ikarus_value_base_is_undefined(V const * value) { + return boost::variant2::holds_alternative(value->data); +} + +template +void ikarus_value_base_set_undefined(V * value, bool undefined) { + if (undefined) { + value->data = boost::variant2::monostate{}; + } else { + value->data = typename V::data_type{}; + } +} + +template +bool ikarus_value_base_is_equal(V const * lhs, V const * rhs) { + return lhs->data == rhs->data; +} + +template +V * ikarus_value_base_copy(V const * value) { + return new V{*value}; +} + template struct IkarusValue * ikarus_value_base_to_value(V * value) { return static_cast(value); } + +template +struct IkarusValue const * ikarus_value_base_to_value_const(V const * value) { + return static_cast(value); +} From 785e43d9e6c1147c52426a7e3f1276778f90f465 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 9 Dec 2023 14:02:05 +0100 Subject: [PATCH 108/166] fixup compiler errors & finalize json (de-)serialization for values Signed-off-by: Folling --- include/ikarus/objects/entity.h | 2 +- include/ikarus/values/number_value.h | 6 +-- src/objects/properties/property.cpp | 8 ++-- src/redirect/json.hpp | 3 ++ src/values/number_value.cpp | 11 ++--- src/values/number_value.hpp | 9 ++-- src/values/text_value.cpp | 8 +--- src/values/text_value.hpp | 2 +- src/values/toggle_value.cpp | 12 +---- src/values/toggle_value.hpp | 2 +- src/values/value.cpp | 40 ++++++++++++----- src/values/value.hpp | 4 +- src/values/value_base.hpp | 67 +++++++++++++++++++++++++--- 13 files changed, 116 insertions(+), 58 deletions(-) create mode 100644 src/redirect/json.hpp diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 2ac9087..5ce2266 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -112,7 +112,7 @@ IKA_API void ikarus_entity_get_properties( ); /// \brief Gets the value of a property of an entity. -/// \details If the entity has never set the value of the property, the default value is returned (which may be indeterminate). +/// \details If the entity has never set the value of the property, the default value is returned (which may be undefined). /// \param entity The entity to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index 7049af8..cedd61d 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -25,7 +25,7 @@ IKA_API IkarusNumberValue * ikarus_number_value_create(); /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. /// \return The underlying data or null if an error occurs or the value is undefined. -IKA_API long double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx); +IKA_API double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx); /// \brief Fetches the size of the underlying data of a number value. /// \param value The number value. @@ -39,7 +39,7 @@ IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); /// \param idx The index of the data to set. /// \pre \li Must be less than the size of the value. /// \param new_data The new data. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double const * new_data); +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double const * new_data); /// \brief Removes a data from a number value. /// \param value The number value. @@ -54,7 +54,7 @@ IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. /// \param new_data The new data. -IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, long double const * new_data); +IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, double const * new_data); /// \brief Clears a number value. /// \param value The number value. diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index d1eb94a..00ab888 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -91,8 +91,9 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p auto * ctx = property->get_project()->get_function_context(); - VTRY( + VTRYRV( auto const source, + nullptr, property->get_project() ->get_db() ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->get_id()) @@ -129,8 +130,9 @@ IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) auto * ctx = property->get_project()->get_function_context(); - VTRY( + VTRYRV( auto const value, + nullptr, property->get_project() ->get_db() ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->get_id()) @@ -144,7 +146,7 @@ IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) }) ); - return new IkarusValue(property->get_project(), value); + return IkarusValue::from_json(value).unwrap_value_or(nullptr); } void ikarus_property_visit( diff --git a/src/redirect/json.hpp b/src/redirect/json.hpp new file mode 100644 index 0000000..c45bde6 --- /dev/null +++ b/src/redirect/json.hpp @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp index cef3508..b408925 100644 --- a/src/values/number_value.cpp +++ b/src/values/number_value.cpp @@ -12,7 +12,7 @@ IkarusNumberValue * ikarus_number_value_create() { return new IkarusNumberValue{}; } -long double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx) { +double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx) { return ikarus_value_base_get(value, idx); } @@ -20,7 +20,7 @@ size_t ikarus_number_value_get_size(IkarusNumberValue const * value) { return ikarus_value_base_get_size(value); } -void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, long double new_data) { +void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double new_data) { return ikarus_value_base_set(value, idx, new_data); } @@ -45,12 +45,7 @@ void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined } char const * ikarus_number_value_to_string(IkarusNumberValue const * value) { - return boost::variant2::visit( - boost::make_overloaded_function( - [](boost::variant2::monostate const&) { return nullptr; }, [](auto const& data) { return fmt::join(data, ", "); } - ), - value->data - ); + return ikarus_value_base_to_string(value, [](auto const& value) { return value; }); } bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs) { diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index 6cad130..16f99c7 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -1,13 +1,13 @@ #pragma once -#include +#include #include #include struct IkarusNumberValue final : IkarusValue { public: - using data_type = long double; + using data_type = double; public: explicit IkarusNumberValue(); @@ -21,7 +21,6 @@ public: ~IkarusNumberValue() override = default; public: - boost::variant2:: - variant> - data{}; + boost::variant2::variant> data{ + }; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp index 08ad03a..ff09770 100644 --- a/src/values/text_value.cpp +++ b/src/values/text_value.cpp @@ -1,5 +1,6 @@ #include "ikarus/values/text_value.h" +#include #include #include @@ -45,12 +46,7 @@ void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined) { } char const * ikarus_text_value_to_string(IkarusTextValue const * value) { - return boost::variant2::visit( - boost::make_overloaded_function( - [](boost::variant2::monostate const&) { return nullptr; }, [](auto const& data) { return fmt::join(data, ", "); } - ), - value->data - ); + return ikarus_value_base_to_string(value, [](auto const& value) { return value; }); } bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs) { diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 8d063f2..1ff74ac 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp index 37b2069..c5ced27 100644 --- a/src/values/toggle_value.cpp +++ b/src/values/toggle_value.cpp @@ -46,17 +46,7 @@ void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined } char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value) { - return boost::variant2::visit( - boost::make_overloaded_function( - [](boost::variant2::monostate const&) { return nullptr; }, - [](auto const& data) { - return fmt::join( - data | boost::adaptors::transformed([](auto const& bool_value) { return bool_value ? "✓" : "✗"; }), ", " - ); - } - ), - value->data - ); + return ikarus_value_base_to_string(value, [](auto const& value) { return value ? "✓" : "✗"; }); } bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs) { diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index 6506dda..20c579c 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/src/values/value.cpp b/src/values/value.cpp index b9a53a7..81cdaa9 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -2,10 +2,11 @@ #include +#include #include -#include -#include +// required for header-only inclusion +#include #include @@ -17,7 +18,7 @@ IkarusValue::IkarusValue(Data data): data(data) {} -cppbase::Result IkarusValue::from_json(boost::json::value const& json) { +cppbase::Result IkarusValue::from_json(boost::json::value const& json) { if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); } else { @@ -35,15 +36,23 @@ cppbase::Result IkarusValue::from_json( } auto create_value = [data]() -> cppbase::Result { - auto * ret = new T{}; - auto res = boost::json::try_value_to>(*data); + T * ret = nullptr; - if (res.has_error()) { - return cppbase::err(FromJsonError{}); + if (data->is_null()) { + ret = new T{}; + ret->data = boost::variant2::monostate{}; + } else { + auto res = boost::json::try_value_to< + boost::container::small_vector>(*data); + + if (res.has_error()) { + return cppbase::err(FromJsonError{}); + } + + ret = new T{}; + ret->data = std::move(res.value()); } - ret->data = std::move(res.value()); - return cppbase::ok(ret); }; @@ -74,7 +83,18 @@ boost::json::value IkarusValue::to_json() const { data ); - auto data_json = boost::variant2::visit([](auto const * value) { return boost::json::value_from(value->data); }, data); + auto data_json = boost::variant2::visit( + [](T const * value) -> boost::json::value { + return boost::variant2::visit( + boost::make_overloaded_function( + []([[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 { {"type", type}, diff --git a/src/values/value.hpp b/src/values/value.hpp index 14c2a73..20b4717 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -8,7 +8,7 @@ struct IkarusValue { public: using Data = boost::variant2::variant; - constexpr auto SMALL_VEC_VALUE_SIZE = 8; + constexpr static auto SMALL_VEC_VALUE_SIZE = 8; public: explicit IkarusValue(Data data); @@ -24,7 +24,7 @@ public: public: struct FromJsonError {}; - [[nodiscard]] static cppbase::Result from_json(boost::json::value const& json); + [[nodiscard]] static cppbase::Result from_json(boost::json::value const& json); [[nodiscard]] boost::json::value to_json() const; public: diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp index 04bad48..416be34 100644 --- a/src/values/value_base.hpp +++ b/src/values/value_base.hpp @@ -1,8 +1,19 @@ #pragma once +#include +#include + +#include +#include +#include + template typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { return &(*data)[idx]; } @@ -11,7 +22,11 @@ typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { template size_t ikarus_value_base_get_size(V const * value) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { return data->size(); } @@ -20,28 +35,44 @@ size_t ikarus_value_base_get_size(V const * value) { template void ikarus_value_base_set(V * value, size_t idx, typename V::data_type new_data) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { (*data)[idx] = new_data; } } template void ikarus_value_base_remove(V * value, size_t idx) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { data->erase(data->begin() + idx); } } template void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_data) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { data->insert(data->begin() + idx, new_data); } } template void ikarus_value_base_clear(V * value) { - if (auto * data = value->data.template get_if>(); data != nullptr) { + if (auto * data = + boost::variant2::get_if>( + &value->data + ); + data != nullptr) { data->clear(); } } @@ -56,10 +87,32 @@ void ikarus_value_base_set_undefined(V * value, bool undefined) { if (undefined) { value->data = boost::variant2::monostate{}; } else { - value->data = typename V::data_type{}; + value->data = boost::container::small_vector{}; } } +template F> +char const * ikarus_value_base_to_string(V const * value, F transformer) { + return boost::variant2::visit( + boost::make_overloaded_function( + [](boost::variant2::monostate const&) -> char const * { return nullptr; }, + [&transformer](boost::container::small_vector const& data + ) -> char const * { + auto buffer = fmt::memory_buffer{}; + + fmt::format_to( + std::back_inserter(buffer), + "{}", + fmt::join(data | std::views::transform(std::forward(transformer)), ", ") + ); + + return buffer.data(); + } + ), + value->data + ); +} + template bool ikarus_value_base_is_equal(V const * lhs, V const * rhs) { return lhs->data == rhs->data; From 62b8f1aef8b63cf409090ba0a369b0ed33a75a38 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 10 Dec 2023 02:15:29 +0100 Subject: [PATCH 109/166] remove property settings Signed-off-by: Folling --- .../settings/number_property_settings.h | 31 ------- .../properties/settings/property_settings.h | 93 ------------------- .../settings/text_property_settings.h | 31 ------- .../settings/toggle_property_settings.h | 31 ------- 4 files changed, 186 deletions(-) delete mode 100644 include/ikarus/objects/properties/settings/number_property_settings.h delete mode 100644 include/ikarus/objects/properties/settings/property_settings.h delete mode 100644 include/ikarus/objects/properties/settings/text_property_settings.h delete mode 100644 include/ikarus/objects/properties/settings/toggle_property_settings.h diff --git a/include/ikarus/objects/properties/settings/number_property_settings.h b/include/ikarus/objects/properties/settings/number_property_settings.h deleted file mode 100644 index fcdaee8..0000000 --- a/include/ikarus/objects/properties/settings/number_property_settings.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file number_property_settings.h -/// \author Folling - -#include - -/// \addtogroup property_settings PropertySettings -/// \brief Number property settings add additional constraints to number properties. -/// \details The following settings are available: -/// -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusNumberPropertySettings; - -/// \brief Sets the default value for a number property. -/// \param settings The number property settings. -/// \pre \li Must not be null. -/// \param default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \remark The settings take ownership of the value, the caller must not free it. -IKA_API void ikarus_number_property_settings_set_default_value( - struct IkarusNumberPropertySettings * settings, struct IkarusNumberValue * default_value -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/settings/property_settings.h b/include/ikarus/objects/properties/settings/property_settings.h deleted file mode 100644 index a47541e..0000000 --- a/include/ikarus/objects/properties/settings/property_settings.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -/// \file property_settings.h -/// \author Folling - -#include - -/// \defgroup property_settings PropertySettings -/// \brief Property settings add additional constraints to properties. -/// \details Each property has a certain set of settings. The options available depend on the type of the property. -/// Settings can be changed after the property has been created. Examples of settings are: -/// Note that the default value must be set using the concrete subtypes to ascertain type correctness. -/// - Minimum: The minimum value for a number property. -/// - Matches: A regular expression that the value of a text property must match. -/// There are also some common settings, shared among all properties: -/// - Default Value (default: PropertyType's default default (sic) value): The value that is returned if no value is specified -/// for some entity. -/// - List (default: false): If set to true, the property becomes a list. Instead of one number, you could then specify a series -/// of numbers. Note that each element in the list is subject to changes in values. E.g. when we say that all values are changed -/// in some way (e.g. reset to the default value), this applies to all elements in the list. -/// - Allow undefined (default: false): If set to true, you can specify an "unknown" value for a property. -/// It might not be known if a character is dead or not for example. -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusPropertySettings; - -/// \brief Gets the default value of a property. -/// \param settings The settings to get the default value of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The default value of the property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue const * ikarus_property_settings_get_default_value(IkarusPropertySettings const * settings); - -/// \brief Fetches whether a property is a list. -/// \param settings The property settings. -/// \pre \li Must not be null. -/// \return True if the property is a list, false otherwise. -IKA_API bool ikarus_property_settings_is_list(IkarusPropertySettings const * settings); - -/// \brief Sets whether a property is a list. -/// \details Noop if unchanged. A change from list -> single truncates to just the first element. A change from single -> list -/// sets the first element to the current value. -/// \param settings The property settings. -/// \pre \li Must not be null. -/// \param list Whether the property should be -/// a list. -IKA_API void ikarus_property_settings_set_is_list(IkarusPropertySettings * settings, bool list); - -/// \brief Fetches whether a property may be undefined. -/// \param settings The property settings. -/// \pre \li Must not be null. -/// \return True if the property may be undefined, false otherwise. -IKA_API bool ikarus_property_settings_may_be_undefined(IkarusPropertySettings const * settings); - -/// \brief Sets whether a property may be undefined. -/// \details Noop if unchanged. If the transition is from undefined -> defined, all undefined values will be reset to the -/// default value. -/// \param settings The property settings. -/// \pre \li Must not be null. -/// \param allow_undefined Whether the property should be allowed to be undefined. -/// \param allow_undefined Whether the property should be allowed to be undefined. -IKA_API void ikarus_property_settings_set_may_be_undefined(IkarusPropertySettings * settings, bool allow_undefined); - -/// \brief Visits a property settings. Calling the appropriate function for the property's type. -/// \param settings The property settings. -/// \pre \li Must not be null. -/// \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 Data passed to the visitors. -IKA_API void ikarus_property_settings_visit( - struct IkarusPropertySettings * settings, - void (*toggle_property_visitor)(struct IkarusTogglePropertySettings * settings, void * data), - void (*number_property_visitor)(struct IkarusNumberPropertySettings * settings, void * data), - void (*text_property_visitor)(struct IkarusTextPropertySettings * settings, void * data), - void * data -); - -/// \see ikarus_property_settings_visit -IKA_API void ikarus_property_settings_visit_const( - struct IkarusPropertySettings const * settings, - void (*toggle_property_visitor)(struct IkarusTogglePropertySettings const * settings, void * data), - void (*number_property_visitor)(struct IkarusNumberPropertySettings const * settings, void * data), - void (*text_property_visitor)(struct IkarusTextPropertySettings const * settings, void * data), - void * data -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/settings/text_property_settings.h b/include/ikarus/objects/properties/settings/text_property_settings.h deleted file mode 100644 index 926058d..0000000 --- a/include/ikarus/objects/properties/settings/text_property_settings.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file text_property_settings.h -/// \author Folling - -#include - -/// \addtogroup property_settings PropertySettings -/// \brief Text property settings add additional constraints to text properties. -/// \details The following settings are available: -/// -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusTextPropertySettings; - -/// \brief Sets the default value for a text property. -/// \param settings The text property settings. -/// \pre \li Must not be null. -/// \param default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \remark The settings take ownership of the value, the caller must not free it. -IKA_API void ikarus_text_property_settings_set_default_value( - struct IkarusTextPropertySettings * settings, struct IkarusTextValue * default_value -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/settings/toggle_property_settings.h b/include/ikarus/objects/properties/settings/toggle_property_settings.h deleted file mode 100644 index d372929..0000000 --- a/include/ikarus/objects/properties/settings/toggle_property_settings.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/// \file toggle_property_settings.h -/// \author Folling - -#include - -/// \addtogroup property_settings PropertySettings -/// \brief Toggle property settings add additional constraints to toggle properties. -/// \details The following settings are available: -/// -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusTogglePropertySettings; - -/// \brief Sets the default value for a toggle property. -/// \param settings The toggle property settings. -/// \pre \li Must not be null. -/// \param default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \remark The settings take ownership of the value, the caller must not free it. -IKA_API void ikarus_toggle_property_settings_set_default_value( - struct IkarusTogglePropertySettings * settings, struct IkarusToggleValue * default_value -); - -IKARUS_END_HEADER - -/// @} From 700d9a92f93285f2e096f1385ecd89918c0e0d3f Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 10 Dec 2023 02:16:30 +0100 Subject: [PATCH 110/166] remove json redirect helper Signed-off-by: Folling --- src/redirect/json.hpp | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/redirect/json.hpp diff --git a/src/redirect/json.hpp b/src/redirect/json.hpp deleted file mode 100644 index c45bde6..0000000 --- a/src/redirect/json.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include From 8fa56decd0a8f3132fd64b861b1e6b516fc7ef4c Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 11 Dec 2023 16:16:34 +0100 Subject: [PATCH 111/166] implement IkarusPropertySource and switch to cppbase::overloaded Signed-off-by: Folling --- src/objects/blueprint.hpp | 2 +- src/objects/entity.hpp | 2 +- src/objects/properties/property_source.cpp | 46 ++++++++++++++++++++++ src/objects/properties/property_source.hpp | 16 +++----- src/objects/properties/toggle_property.hpp | 2 +- src/values/number_value.hpp | 6 +-- src/values/text_value.hpp | 6 +-- src/values/toggle_value.hpp | 6 +-- src/values/value.cpp | 21 +++++----- src/values/value_base.hpp | 33 ++++++++-------- 10 files changed, 90 insertions(+), 50 deletions(-) diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index 41042ba..6fa6281 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -2,7 +2,7 @@ #include -struct IkarusBlueprint final : IkarusObject { +struct IkarusBlueprint : IkarusObject { IkarusBlueprint(struct IkarusProject * project, IkarusId id); IkarusBlueprint(IkarusBlueprint const&) = default; diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index fb78e39..f6d4f90 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -2,7 +2,7 @@ #include -struct IkarusEntity final : IkarusObject { +struct IkarusEntity : IkarusObject { inline IkarusEntity(struct IkarusProject * project, IkarusId id): IkarusObject{project, id} {} diff --git a/src/objects/properties/property_source.cpp b/src/objects/properties/property_source.cpp index e69de29..364ca84 100644 --- a/src/objects/properties/property_source.cpp +++ b/src/objects/properties/property_source.cpp @@ -0,0 +1,46 @@ +#include "property_source.hpp" + +#include + +#include + +IkarusPropertySource::IkarusPropertySource(Data data): + data{data} {} + +IkarusPropertySource * ikarus_property_source_create_blueprint(IkarusBlueprint * blueprint) { + return new IkarusPropertySource{blueprint}; +} + +IkarusPropertySource * ikarus_property_source_create_entity(IkarusEntity * entity) { + return new IkarusPropertySource{entity}; +} + +void ikarus_property_source_visit( + struct IkarusPropertySource * property_source, + void (*blueprint_visitor)(struct IkarusBlueprint *, void *), + void (*entity_visitor)(struct IkarusEntity *, void *), + void * user_data +) { + boost::variant2::visit( + boost::make_overloaded_function( + [blueprint_visitor, user_data](IkarusBlueprint * blueprint) { blueprint_visitor(blueprint, user_data); }, + [entity_visitor, user_data](IkarusEntity * entity) { entity_visitor(entity, user_data); } + ), + property_source->data + ); +} + +void ikarus_property_source_visit_const( + struct IkarusPropertySource const * property_source, + void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), + void (*entity_visitor)(struct IkarusEntity const *, void *), + void * user_data +) { + boost::variant2::visit( + boost::make_overloaded_function( + [blueprint_visitor, user_data](IkarusBlueprint const * blueprint) { blueprint_visitor(blueprint, user_data); }, + [entity_visitor, user_data](IkarusEntity const * entity) { entity_visitor(entity, user_data); } + ), + property_source->data + ); +} diff --git a/src/objects/properties/property_source.hpp b/src/objects/properties/property_source.hpp index 8da517a..0fda4ed 100644 --- a/src/objects/properties/property_source.hpp +++ b/src/objects/properties/property_source.hpp @@ -1,16 +1,15 @@ #pragma once -#include +#include #include struct IkarusPropertySource { public: - using Data = std::variant; + using Data = boost::variant2::variant; public: - inline explicit IkarusPropertySource(Data data): - _data{data} {} + explicit IkarusPropertySource(Data data); IkarusPropertySource(IkarusPropertySource const&) = default; IkarusPropertySource(IkarusPropertySource&&) = default; @@ -18,13 +17,8 @@ public: IkarusPropertySource& operator=(IkarusPropertySource const&) = default; IkarusPropertySource& operator=(IkarusPropertySource&&) = default; - ~IkarusPropertySource() = default; + virtual ~IkarusPropertySource() = default; public: - [[nodiscard]] inline Data const& get_data() const { - return _data; - } - -private: - std::variant _data; + Data data; }; diff --git a/src/objects/properties/toggle_property.hpp b/src/objects/properties/toggle_property.hpp index 2ca2520..92bbbc4 100644 --- a/src/objects/properties/toggle_property.hpp +++ b/src/objects/properties/toggle_property.hpp @@ -2,7 +2,7 @@ #include -struct IkarusToggleProperty final : IkarusProperty { +struct IkarusToggleProperty : IkarusProperty { public: IkarusToggleProperty(struct IkarusProject * project, IkarusId id); }; diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index 16f99c7..1421ca3 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -5,9 +5,9 @@ #include -struct IkarusNumberValue final : IkarusValue { +struct IkarusNumberValue : IkarusValue { public: - using data_type = double; + using DataType = double; public: explicit IkarusNumberValue(); @@ -21,6 +21,6 @@ public: ~IkarusNumberValue() override = default; public: - boost::variant2::variant> data{ + boost::variant2::variant> data{ }; }; diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 1ff74ac..117e175 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -4,9 +4,9 @@ #include -struct IkarusTextValue final : IkarusValue { +struct IkarusTextValue : IkarusValue { public: - using data_type = std::string; + using DataType = std::string; public: explicit IkarusTextValue(); @@ -20,6 +20,6 @@ public: ~IkarusTextValue() override = default; public: - boost::variant2::variant> data{ + boost::variant2::variant> data{ }; }; diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index 20c579c..7e2240f 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -4,9 +4,9 @@ #include -struct IkarusToggleValue final : IkarusValue { +struct IkarusToggleValue : IkarusValue { public: - using data_type = bool; + using DataType = bool; public: explicit IkarusToggleValue(); @@ -20,6 +20,6 @@ public: ~IkarusToggleValue() override = default; public: - boost::variant2::variant> data{ + boost::variant2::variant> data{ }; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index 81cdaa9..2cffcaa 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -3,9 +3,10 @@ #include #include -#include // required for header-only inclusion +#include "cppbase/templates.hpp" + #include #include @@ -43,7 +44,7 @@ cppbase::Result IkarusValue::from_jso ret->data = boost::variant2::monostate{}; } else { auto res = boost::json::try_value_to< - boost::container::small_vector>(*data); + boost::container::small_vector>(*data); if (res.has_error()) { return cppbase::err(FromJsonError{}); @@ -75,21 +76,21 @@ cppbase::Result IkarusValue::from_jso boost::json::value IkarusValue::to_json() const { auto type = boost::variant2::visit( - boost::make_overloaded_function( + cppbase::overloaded{ []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusPropertyType_Toggle; }, []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusPropertyType_Number; }, []([[maybe_unused]] IkarusTextValue const * value) { return IkarusPropertyType_Text; } - ), + }, data ); auto data_json = boost::variant2::visit( [](T const * value) -> boost::json::value { return boost::variant2::visit( - boost::make_overloaded_function( + 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 ); }, @@ -110,11 +111,11 @@ void ikarus_value_visit( void * data ) { boost::variant2::visit( - boost::make_overloaded_function( + cppbase::overloaded{ [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); } - ), + }, value->data ); } @@ -127,11 +128,11 @@ void ikarus_value_visit_const( void * data ) { boost::variant2::visit( - boost::make_overloaded_function( + cppbase::overloaded{ [toggle_visitor, data](IkarusToggleValue const * toggle_value) { toggle_visitor(toggle_value, data); }, [number_visitor, data](IkarusNumberValue const * number_value) { number_visitor(number_value, data); }, [text_visitor, data](IkarusTextValue const * text_value) { text_visitor(text_value, data); } - ), + }, value->data ); } diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp index 416be34..46cf457 100644 --- a/src/values/value_base.hpp +++ b/src/values/value_base.hpp @@ -1,16 +1,15 @@ #pragma once -#include #include -#include +#include + #include -#include template -typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { +typename V::DataType const * ikarus_value_base_get(V * value, size_t idx) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -23,7 +22,7 @@ typename V::data_type const * ikarus_value_base_get(V * value, size_t idx) { template size_t ikarus_value_base_get_size(V const * value) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -34,9 +33,9 @@ size_t ikarus_value_base_get_size(V const * value) { } template -void ikarus_value_base_set(V * value, size_t idx, typename V::data_type new_data) { +void ikarus_value_base_set(V * value, size_t idx, typename V::DataType new_data) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -47,7 +46,7 @@ void ikarus_value_base_set(V * value, size_t idx, typename V::data_type new_data template void ikarus_value_base_remove(V * value, size_t idx) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -56,9 +55,9 @@ void ikarus_value_base_remove(V * value, size_t idx) { } template -void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_data) { +void ikarus_value_base_insert(V * value, size_t idx, typename V::DataType new_data) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -69,7 +68,7 @@ void ikarus_value_base_insert(V * value, size_t idx, typename V::data_type new_d template void ikarus_value_base_clear(V * value) { if (auto * data = - boost::variant2::get_if>( + boost::variant2::get_if>( &value->data ); data != nullptr) { @@ -87,16 +86,16 @@ void ikarus_value_base_set_undefined(V * value, bool undefined) { if (undefined) { value->data = boost::variant2::monostate{}; } else { - value->data = boost::container::small_vector{}; + value->data = boost::container::small_vector{}; } } -template F> +template F> char const * ikarus_value_base_to_string(V const * value, F transformer) { return boost::variant2::visit( - boost::make_overloaded_function( + cppbase::overloaded { [](boost::variant2::monostate const&) -> char const * { return nullptr; }, - [&transformer](boost::container::small_vector const& data + [&transformer](auto const& data ) -> char const * { auto buffer = fmt::memory_buffer{}; @@ -108,7 +107,7 @@ char const * ikarus_value_base_to_string(V const * value, F transformer) { return buffer.data(); } - ), + }, value->data ); } From 484a505f2c52c3deead3a3c564913f06ad440052 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 26 Dec 2023 13:09:41 +0100 Subject: [PATCH 112/166] make object fields public and fixup compile errors Signed-off-by: Folling --- .clang-tidy | 2 +- include/ikarus/objects/blueprint.h | 24 +-- include/ikarus/objects/entity.h | 6 +- include/ikarus/objects/object_type.h | 6 + src/objects/blueprint.cpp | 251 +++++++++++---------------- src/objects/entity.cpp | 21 +++ src/objects/object.cpp | 16 +- src/objects/object.hpp | 15 +- src/objects/object_helper.hpp | 68 ++++++++ src/objects/object_type.cpp | 12 ++ src/objects/properties/property.cpp | 32 ++-- src/persistence/project.cpp | 9 +- src/persistence/project.hpp | 12 +- 13 files changed, 258 insertions(+), 216 deletions(-) create mode 100644 src/objects/object_helper.hpp create mode 100644 src/objects/object_type.cpp diff --git a/.clang-tidy b/.clang-tidy index fa1f43f..7262427 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,6 +1,6 @@ Checks: >- -*, - bugprone-*, + bugprone-*, -bugprone-lambda-function-name, cppcoreguidelines-*, -cppcoreguidelines-owning-memory, clang-analyzer-*, google-*, -google-readability-todo, diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 57d1a7c..ce83160 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -35,13 +35,6 @@ IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project /// \remark The blueprint must not be accessed after deletion. IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); -/// \brief Gets the number of properties of a blueprint. -/// \param blueprint The blueprint to get the number of properties of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint); - /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. /// \pre \li Must not be null. @@ -49,16 +42,17 @@ IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * bluep /// \param properties_out The buffer to write the properties to. /// \pre \li Must not be null. /// \param properties_out_size The size of the buffer. +/// \see ikarus_blueprint_get_property_count IKA_API void ikarus_blueprint_get_properties( IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size ); -/// \brief Gets the number of entities linked to a blueprint. -/// \param blueprint The blueprint to get the number of linked entities of. +/// \brief Gets the number of properties of a blueprint. +/// \param blueprint The blueprint to get the number of properties of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \return The number of linked entities or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint); +/// \return The number of properties or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint); /// \brief Gets the entities linked to a blueprint. /// \param blueprint The blueprint to get the linked entities of. @@ -67,10 +61,18 @@ IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * /// \param entities_out The buffer to write the entities to. /// \pre \li Must not be null. /// \param entities_out_size The size of the buffer. +/// \see ikarus_blueprint_get_linked_entity_count IKA_API void ikarus_blueprint_get_linked_entities( IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size ); +/// \brief Gets the number of entities linked to a blueprint. +/// \param blueprint The blueprint to get the number of linked entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \return The number of linked entities or undefined if an error occurs. +IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint); + /// \brief Casts a blueprint to an object. /// \param blueprint The blueprint to cast. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 5ce2266..782452e 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -45,13 +45,9 @@ struct IkarusEntity; /// \param name The name of the entity. /// \pre \li Must not be null. /// \pre \li Must not be empty. -/// \param blueprints Blueprints to link the entity to (0..n). Null is treated as an empty array. -/// \param blueprints_count The number of blueprints. /// \return The created entity or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusEntity * ikarus_entity_create( - struct IkarusProject * project, char const * name, struct IkarusBlueprint ** blueprints, size_t blueprints_count -); +IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name); /// \brief Deletes an entity. /// \param entity The entity to delete. diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index efb6609..4298a9b 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -22,6 +22,12 @@ enum IkarusObjectType { IkarusObjectType_Property = 0b00000011, }; +/// \brief Converts an IkarusObjectType to a string. +/// \param type The type to convert. +/// \return The string representation of the type. +/// \remark The returned string must not be freed. +char const * ikarus_object_type_to_string(IkarusObjectType type); + IKARUS_END_HEADER /// @} diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index bdbea93..fc49c71 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -1,13 +1,16 @@ +#include "ikarus/objects/blueprint.h" + +#include "objects/blueprint.hpp" + #include #include #include #include -#include - -#include #include +#include +#include #include #include @@ -26,40 +29,9 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c return nullptr; } - VTRYRV( - auto const id, - nullptr, - project->get_db() - ->transact([name](auto * db) -> cppbase::Result { - LOG_VERBOSE("creating blueprint in objects table"); - - TRY(db->execute( - "INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", - static_cast(IkarusObjectType_Blueprint), - name - )); - - auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - - LOG_DEBUG("blueprint is {}", id); - - LOG_VERBOSE("inserting blueprint into blueprints table"); - - TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?);", id)); - - return cppbase::ok(id); - }) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("unable to insert blueprint into database: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_VERBOSE("successfully created blueprint"); + VTRYRV(auto const id, nullptr, insert_object(project, ctx, IkarusObjectType_Blueprint, name, [](auto * db, IkarusId id) { + return db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id); + })); return project->get_blueprint(id); } @@ -67,60 +39,11 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { LOG_INFO("deleting blueprint"); - LOG_DEBUG("project={};blueprint={}", blueprint->get_project()->get_path().c_str(), blueprint->get_id()); + LOG_DEBUG("project={}; blueprint={}", blueprint->project->get_path().c_str(), blueprint->id); - auto * ctx = blueprint->get_project()->get_function_context(); + auto * ctx = blueprint->project->get_function_context(); - TRYRV( - , - blueprint->get_project() - ->get_db() - ->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->get_id()) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to delete blueprint from objects table: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_VERBOSE("blueprint was successfully deleted from database, freeing"); - - blueprint->get_project()->uncache_blueprint(blueprint); - - LOG_VERBOSE("successfully deleted blueprint"); -} - -size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { - LOG_VERBOSE("fetching blueprint property count"); - - auto * ctx = blueprint->get_project()->get_function_context(); - - LOG_DEBUG("blueprint={}", blueprint->get_id()); - - VTRYRV( - auto count, - 0, - blueprint->get_project() - ->get_db() - ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->get_id()) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint property count: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint property count: {}", count); - - LOG_VERBOSE("successfully fetched blueprint property count"); - - return static_cast(count); + delete_object(blueprint->project, ctx, blueprint); } void ikarus_blueprint_get_properties( @@ -129,25 +52,25 @@ void ikarus_blueprint_get_properties( LOG_VERBOSE("fetching blueprint properties"); LOG_DEBUG( - "project={};blueprint={};properties_out_size={}", - blueprint->get_project()->get_path().c_str(), - blueprint->get_id(), + "project={}; blueprint={}; properties_out_size={}", + blueprint->project->get_path().c_str(), + blueprint->id, properties_out_size ); - auto * ctx = blueprint->get_project()->get_function_context(); + auto * ctx = blueprint->project->get_function_context(); IkarusId ids[properties_out_size]; TRYRV( , - blueprint->get_project() + blueprint->project ->get_db() ->query_many_buffered( "SELECT `id` FROM `properties` WHERE `source` = ?", static_cast(ids), properties_out_size, - blueprint->get_id() + blueprint->id ) .on_error([ctx](auto const& err) { ctx->set_error( @@ -167,7 +90,7 @@ void ikarus_blueprint_get_properties( VTRYRV( auto const type, , - IkarusProperty::get_property_type(blueprint->get_project(), id).on_error([ctx, id](auto const& err) { + IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const& err) { ctx->set_error( fmt::format("failed to fetch property {}'s type: {}", id, err), true, @@ -178,25 +101,101 @@ void ikarus_blueprint_get_properties( ); /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - properties_out[i] = blueprint->get_project()->get_property(id, type); + properties_out[i] = blueprint->project->get_property(id, type); } LOG_VERBOSE("successfully fetched blueprint properties"); } -size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { - LOG_VERBOSE("fetching blueprint linked entity count"); +size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { + LOG_VERBOSE("fetching blueprint property count"); - LOG_DEBUG("project={};blueprint={}", blueprint->get_project()->get_path().c_str(), blueprint->get_id()); + auto * ctx = blueprint->project->get_function_context(); - auto * ctx = blueprint->get_project()->get_function_context(); + LOG_DEBUG("blueprint={}", blueprint->id); VTRYRV( auto count, 0, - blueprint->get_project() + blueprint->project ->get_db() - ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->get_id()) + ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->id) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint property count: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_DEBUG("blueprint property count: {}", count); + + LOG_VERBOSE("successfully fetched blueprint property count"); + + return static_cast(count); +} + +void ikarus_blueprint_get_linked_entities( + IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size +) { + LOG_VERBOSE("fetching blueprint linked entities"); + + LOG_DEBUG( + "project={}; blueprint={}; entities_out_size={}", + blueprint->project->get_path().c_str(), + blueprint->id, + entities_out_size + ); + + auto * ctx = blueprint->project->get_function_context(); + + IkarusId ids[entities_out_size]; + + TRYRV( + , + blueprint->project + ->get_db() + ->query_many_buffered( + "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", + static_cast(ids), + entities_out_size, + blueprint->id + ) + .on_error([ctx](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch blueprint linked entity ids: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_DEBUG("blueprint linked entities: [{}]", fmt::join(ids, ids + entities_out_size, ", ")); + + for (size_t i = 0; i < entities_out_size; ++i) { + /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + entities_out[i] = blueprint->project->get_entity(ids[i]); + } + + LOG_VERBOSE("successfully fetched blueprint linked entities"); +} + +size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { + LOG_VERBOSE("fetching blueprint linked entity count"); + + LOG_DEBUG("project={}; blueprint={}", blueprint->project->get_path().c_str(), blueprint->id); + + auto * ctx = blueprint->project->get_function_context(); + + VTRYRV( + auto count, + 0, + blueprint->project + ->get_db() + ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->id) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch blueprint linked entity count: {}", err), @@ -214,52 +213,6 @@ size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprin return static_cast(count); } -void ikarus_blueprint_get_linked_entities( - IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size -) { - LOG_VERBOSE("fetching blueprint linked entities"); - - LOG_DEBUG( - "project={};blueprint={};entities_out_size={}", - blueprint->get_project()->get_path().c_str(), - blueprint->get_id(), - entities_out_size - ); - - auto * ctx = blueprint->get_project()->get_function_context(); - - IkarusId ids[entities_out_size]; - - TRYRV( - , - blueprint->get_project() - ->get_db() - ->query_many_buffered( - "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", - static_cast(ids), - entities_out_size, - blueprint->get_id() - ) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint linked entity ids: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint linked entities: [{}]", fmt::join(ids, ids + entities_out_size, ", ")); - - for (size_t i = 0; i < entities_out_size; ++i) { - /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - entities_out[i] = blueprint->get_project()->get_entity(ids[i]); - } - - LOG_VERBOSE("successfully fetched blueprint linked entities"); -} - IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint) { return static_cast(blueprint); } diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp index e69de29..ce5d853 100644 --- a/src/objects/entity.cpp +++ b/src/objects/entity.cpp @@ -0,0 +1,21 @@ +#include "entity.hpp" + +#include + +#include +#include + +IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name) { + LOG_INFO("creating new entity"); + + LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); + + auto * ctx = project->get_function_context(); + + if (cppbase::is_empty_or_blank(name)) { + ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return nullptr; + } + + // TODO +} diff --git a/src/objects/object.cpp b/src/objects/object.cpp index 2ce961e..204c7a7 100644 --- a/src/objects/object.cpp +++ b/src/objects/object.cpp @@ -1,17 +1,5 @@ #include "object.hpp" IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): - _project{project}, - _id{id} {} - -IkarusProject * IkarusObject::get_project() { - return _project; -} - -IkarusProject * IkarusObject::get_project() const { - return _project; -} - -IkarusId IkarusObject::get_id() const { - return _id; -} + project{project}, + id{id} {} diff --git a/src/objects/object.hpp b/src/objects/object.hpp index bb28da5..5a94aa2 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -1,5 +1,9 @@ #pragma once +#include + +#include + #include struct IkarusObject { @@ -15,13 +19,6 @@ public: virtual ~IkarusObject() = default; public: - [[nodiscard]] struct IkarusProject * get_project(); - - [[nodiscard]] struct IkarusProject * get_project() const; - - [[nodiscard]] IkarusId get_id() const; - -private: - struct IkarusProject mutable * _project; - IkarusId _id; + struct IkarusProject * project; + IkarusId id; }; diff --git a/src/objects/object_helper.hpp b/src/objects/object_helper.hpp new file mode 100644 index 0000000..0fe9df7 --- /dev/null +++ b/src/objects/object_helper.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include + +#include + +#include +#include + +#include + +template F> +[[nodiscard]] cppbase::Result insert_object( + IkarusProject * project, FunctionContext * ctx, IkarusObjectType type, std::string_view name, F insert_function +) { + char const * object_type_str = ikarus_object_type_to_string(type); + + VTRY( + auto const id, + project->get_db() + ->transact([&](auto * db) -> cppbase::Result { + LOG_VERBOSE("creating {} in objects table", object_type_str); + + TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(type), name)); + + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); + + LOG_DEBUG("{} is {}", object_type_str, id); + + TRY(insert_function(db, id)); + + return cppbase::ok(id); + }) + .on_error([&](auto const& err) { + ctx->set_error( + fmt::format("unable to insert {} into database: {}", object_type_str, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_VERBOSE("successfully created blueprint"); + + return cppbase::ok(id); +} + +template +void delete_object(IkarusProject * project, FunctionContext * ctx, T * object) { + auto id = object->id; + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(id)); + + TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", id).on_error([&](auto const& err) { + ctx->set_error( + fmt::format("failed to delete {} from objects table: {}", object_type_str, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + })); + + LOG_VERBOSE("{} was successfully deleted from database, freeing", object_type_str); + + project->uncache(object); + + LOG_VERBOSE("successfully deleted {}", object_type_str); +} diff --git a/src/objects/object_type.cpp b/src/objects/object_type.cpp new file mode 100644 index 0000000..fb7d825 --- /dev/null +++ b/src/objects/object_type.cpp @@ -0,0 +1,12 @@ +#include "ikarus/objects/object_type.h" + +char const * ikarus_object_type_to_string(IkarusObjectType type) { + switch (type) { + case IkarusObjectType_None: return "none"; + case IkarusObjectType_Entity: return "entity"; + case IkarusObjectType_Blueprint: return "blueprint"; + case IkarusObjectType_Property: return "property"; + } + + return "unknown"; +} diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index 00ab888..2abef0a 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -51,15 +51,15 @@ cppbase::Result IkarusProperty: IKA_API void ikarus_property_delete(IkarusProperty * property) { LOG_INFO("deleting property"); - LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->get_project()->get_function_context(); + auto * ctx = property->project->get_function_context(); TRYRV( , - property->get_project() + property->project ->get_db() - ->execute("DELETE FROM `objects` WHERE `id` = ?", property->get_id()) + ->execute("DELETE FROM `objects` WHERE `id` = ?", property->id) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to delete property from objects table: {}", err), @@ -72,7 +72,7 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { LOG_VERBOSE("property was successfully deleted from database, freeing"); - property->get_project()->uncache_property(property); + property->project->uncache(property); LOG_VERBOSE("successfully deleted property"); } @@ -80,23 +80,23 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { LOG_VERBOSE("fetching property type"); - return IkarusProperty::get_property_type(property->get_project(), property->get_id()) + return IkarusProperty::get_property_type(property->project, property->id) .unwrap_value_or(IkarusPropertyType_Toggle); } IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { LOG_VERBOSE("fetching property source"); - LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->get_project()->get_function_context(); + auto * ctx = property->project->get_function_context(); VTRYRV( auto const source, nullptr, - property->get_project() + property->project ->get_db() - ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->get_id()) + ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch property's source: {}", err), @@ -108,8 +108,8 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p ); switch (ikarus_id_get_object_type(source)) { - case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->get_project()->get_blueprint(source)}; - case IkarusObjectType_Entity: return new IkarusPropertySource{property->get_project()->get_entity(source)}; + case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->project->get_blueprint(source)}; + case IkarusObjectType_Entity: return new IkarusPropertySource{property->project->get_entity(source)}; default: { ctx->set_error( fmt::format("PropertySource is neither blueprint nor entity"), @@ -126,16 +126,16 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) { LOG_VERBOSE("fetching property default value"); - LOG_VERBOSE("project={};property={}", property->get_project()->get_path().c_str(), property->get_id()); + LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->get_project()->get_function_context(); + auto * ctx = property->project->get_function_context(); VTRYRV( auto const value, nullptr, - property->get_project() + property->project ->get_db() - ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->get_id()) + ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id) .on_error([ctx](auto const& err) { ctx->set_error( fmt::format("failed to fetch property's default value: {}", err), diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index d7d5e5e..163bc93 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -2,7 +2,10 @@ #include "ikarus/persistence/project.h" +#include +#include #include +#include #include #include #include @@ -31,7 +34,7 @@ IkarusBlueprint * IkarusProject::get_blueprint(IkarusId id) { return get_cached_object(id, this->_blueprints); } -auto IkarusProject::uncache_blueprint(IkarusBlueprint * blueprint) -> void { +auto IkarusProject::uncache(IkarusBlueprint * blueprint) -> void { remove_cached_object(blueprint, _blueprints); } @@ -39,7 +42,7 @@ auto IkarusProject::get_entity(IkarusId id) -> IkarusEntity * { return get_cached_object(id, this->_entities); } -auto IkarusProject::uncache_entity(IkarusEntity * entity) -> void { +auto IkarusProject::uncache(IkarusEntity * entity) -> void { remove_cached_object(entity, _entities); } @@ -60,6 +63,6 @@ auto IkarusProject::get_property(IkarusId id, IkarusPropertyType type) -> Ikarus return iter->second.get(); } -auto IkarusProject::uncache_property(IkarusProperty * property) -> void { +auto IkarusProject::uncache(IkarusProperty * property) -> void { remove_cached_object(property, _properties); } diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index d3fc6a1..2af1fff 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -11,10 +11,6 @@ #include #include -#include -#include -#include - constexpr inline auto MAXIMUM_ERROR_INFOS = 8; /// \private @@ -32,13 +28,13 @@ public: public: [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; - auto uncache_blueprint(struct IkarusBlueprint * blueprint) -> void; + auto uncache(struct IkarusBlueprint * blueprint) -> void; [[nodiscard]] auto get_entity(IkarusId id) -> struct IkarusEntity *; - auto uncache_entity(struct IkarusEntity * entity) -> void; + auto uncache(struct IkarusEntity * entity) -> void; [[nodiscard]] auto get_property(IkarusId id, IkarusPropertyType type) -> struct IkarusProperty *; - auto uncache_property(struct IkarusProperty * property) -> void; + auto uncache(struct IkarusProperty * property) -> void; private: template @@ -54,7 +50,7 @@ private: template void remove_cached_object(T * object, std::unordered_map>& cache) { - cache.erase(object->get_id()); + cache.erase(object->id); } private: From 5efc0a6e86ab72876af42f911745015ee214d536 Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 27 Dec 2023 12:52:14 +0100 Subject: [PATCH 113/166] update sqlitecpp Signed-off-by: Folling --- src/objects/object_helper.hpp | 68 --------------- src/objects/util.hpp | 151 ++++++++++++++++++++++++++++++++++ vendor/sqlitecpp | 2 +- 3 files changed, 152 insertions(+), 69 deletions(-) delete mode 100644 src/objects/object_helper.hpp create mode 100644 src/objects/util.hpp diff --git a/src/objects/object_helper.hpp b/src/objects/object_helper.hpp deleted file mode 100644 index 0fe9df7..0000000 --- a/src/objects/object_helper.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include - -#include - -template F> -[[nodiscard]] cppbase::Result insert_object( - IkarusProject * project, FunctionContext * ctx, IkarusObjectType type, std::string_view name, F insert_function -) { - char const * object_type_str = ikarus_object_type_to_string(type); - - VTRY( - auto const id, - project->get_db() - ->transact([&](auto * db) -> cppbase::Result { - LOG_VERBOSE("creating {} in objects table", object_type_str); - - TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(type), name)); - - auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - - LOG_DEBUG("{} is {}", object_type_str, id); - - TRY(insert_function(db, id)); - - return cppbase::ok(id); - }) - .on_error([&](auto const& err) { - ctx->set_error( - fmt::format("unable to insert {} into database: {}", object_type_str, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_VERBOSE("successfully created blueprint"); - - return cppbase::ok(id); -} - -template -void delete_object(IkarusProject * project, FunctionContext * ctx, T * object) { - auto id = object->id; - auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(id)); - - TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", id).on_error([&](auto const& err) { - ctx->set_error( - fmt::format("failed to delete {} from objects table: {}", object_type_str, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - })); - - LOG_VERBOSE("{} was successfully deleted from database, freeing", object_type_str); - - project->uncache(object); - - LOG_VERBOSE("successfully deleted {}", object_type_str); -} diff --git a/src/objects/util.hpp b/src/objects/util.hpp new file mode 100644 index 0000000..963701b --- /dev/null +++ b/src/objects/util.hpp @@ -0,0 +1,151 @@ +#pragma once + +#include + +#include + +#include +#include + +#include + +namespace util { +template F> +[[nodiscard]] cppbase::Result insert_object( + IkarusProject * project, FunctionContext * ctx, IkarusObjectType type, std::string_view name, F insert_function +) { + char const * object_type_str = ikarus_object_type_to_string(type); + + VTRY( + auto const id, + project->get_db() + ->transact([&](auto * db) -> cppbase::Result { + LOG_VERBOSE("creating {} in objects table", object_type_str); + + TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(type), name)); + + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); + + LOG_DEBUG("{} is {}", object_type_str, id); + + TRY(insert_function(db, id)); + + return cppbase::ok(id); + }) + .on_error([&](auto const& err) { + ctx->set_error( + fmt::format("unable to insert {} into database: {}", object_type_str, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_VERBOSE("successfully created blueprint"); + + return cppbase::ok(id); +} + +template +void delete_object(IkarusProject * project, FunctionContext * ctx, T * object) { + auto id = object->id; + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(id)); + + TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", id).on_error([&](auto const& err) { + ctx->set_error( + fmt::format("failed to delete {} from objects table: {}", object_type_str, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + })); + + LOG_VERBOSE("{} was successfully deleted from database, freeing", object_type_str); + + project->uncache(object); + + LOG_VERBOSE("successfully deleted {}", object_type_str); +} + +struct SingleQueryData { + std::string_view table_name; + std::string_view select_field_name; +}; + +template +cppbase::Result fetch_single_field(IkarusObject * object, SingleQueryData const& query_data) { + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); + + LOG_VERBOSE("fetching property default value"); + + LOG_VERBOSE("project={};property={}", object->project->get_path().c_str(), object->id); + + auto * ctx = object->project->get_function_context(); + + VTRY( + T value, + object->project->get_db() + ->query_one( + fmt::format("SELECT `{}` FROM `{}` WHERE `id` = ?", query_data.select_field_name, query_data.table_name), + object->id + ) + .on_error([&](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.select_field_name, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + return value; +} + +struct MultipleBufferQueryData { + std::string_view table_name; + std::string_view select_field_name; + std::string_view where_field_name; +}; + +template +cppbase::Result fetch_multiple_buffered( + IkarusObject * object, + MultipleBufferQueryData const& query_data, + std::string_view relation_desc, + T * buffer, + size_t buffer_size +) { + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); + + LOG_VERBOSE("fetching {} {}", object_type_str, relation_desc); + + LOG_VERBOSE("project={};{}={}", object->project->get_path().c_str(), object_type_str, object->id); + + auto * ctx = object->project->get_function_context(); + + TRY(object->project->get_db() + ->query_many_buffered( + fmt::format( + "SELECT `{}` FROM `{}` WHERE `{}` = ?", + query_data.select_field_name, + query_data.table_name, + query_data.where_field_name + ), + buffer, + buffer_size, + object->id + ) + .on_error([&](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch {} {} from database: {}", object_type_str, relation_desc, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + })); + + return cppbase::ok(); +} +} diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 6e39dd2..2a93b8b 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 6e39dd241f8469784b8a356e7a9563fa58b0e2c4 +Subproject commit 2a93b8b1a8be03a9a9c4c72956b1111299de0ddd From f63d2b2fe28d7acfe697240b872d1db47e1e9e2a Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 27 Dec 2023 12:52:27 +0100 Subject: [PATCH 114/166] simplify blueprint implementation with helpers Signed-off-by: Folling --- src/objects/blueprint.cpp | 240 ++++++++------------------- src/objects/util.hpp | 152 +++++++++++++---- src/persistence/function_context.cpp | 4 +- src/persistence/function_context.hpp | 14 +- src/persistence/project.cpp | 2 +- src/persistence/project.hpp | 6 +- 6 files changed, 200 insertions(+), 218 deletions(-) diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index fc49c71..af5fc08 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -1,16 +1,15 @@ #include "ikarus/objects/blueprint.h" #include "objects/blueprint.hpp" - -#include +#include "util.hpp" #include #include #include #include -#include #include +#include #include #include @@ -18,199 +17,92 @@ IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusObject{project, id} {} IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { - LOG_INFO("creating new blueprint"); - - LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); - - auto * ctx = project->get_function_context(); - - if (cppbase::is_empty_or_blank(name)) { - ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - VTRYRV(auto const id, nullptr, insert_object(project, ctx, IkarusObjectType_Blueprint, name, [](auto * db, IkarusId id) { - return db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id); - })); - - return project->get_blueprint(id); + return ikarus::util::insert_object( + project, + IkarusObjectType_Blueprint, + name, + [](auto * db, IkarusId id) { return db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id); }, + [project](IkarusId id) { return project->get_blueprint(id); } + ).unwrap_value_or(nullptr); } void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { - LOG_INFO("deleting blueprint"); - - LOG_DEBUG("project={}; blueprint={}", blueprint->project->get_path().c_str(), blueprint->id); - - auto * ctx = blueprint->project->get_function_context(); - - delete_object(blueprint->project, ctx, blueprint); + ikarus::util::delete_object(blueprint->project, blueprint); } void ikarus_blueprint_get_properties( IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size ) { - LOG_VERBOSE("fetching blueprint properties"); + ikarus::util::fetch_multiple_buffered( + blueprint, + ikarus::util::MultipleBufferQueryData{ + .table_name = "properties", + .select_field_name = "id", + .where_field_name = "blueprint", + .relation_desc = "properties" + }, + properties_out, + properties_out_size, + [&](IkarusProject * project, IkarusFunctionContext * ctx, IkarusId id + ) -> cppbase::Result { + VTRY( + auto const type, IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch property {}'s type: {}", id, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); - LOG_DEBUG( - "project={}; blueprint={}; properties_out_size={}", - blueprint->project->get_path().c_str(), - blueprint->id, - properties_out_size + return cppbase::ok(project->get_property(id, type)); + } ); - - auto * ctx = blueprint->project->get_function_context(); - - IkarusId ids[properties_out_size]; - - TRYRV( - , - blueprint->project - ->get_db() - ->query_many_buffered( - "SELECT `id` FROM `properties` WHERE `source` = ?", - static_cast(ids), - properties_out_size, - blueprint->id - ) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint property ids: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint properties: [{}]", fmt::join(ids, ids + properties_out_size, ", ")); - - for (size_t i = 0; i < properties_out_size; ++i) { - auto const id = ids[i]; - - VTRYRV( - auto const type, - , - IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch property {}'s type: {}", id, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - properties_out[i] = blueprint->project->get_property(id, type); - } - - LOG_VERBOSE("successfully fetched blueprint properties"); } size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { - LOG_VERBOSE("fetching blueprint property count"); - - auto * ctx = blueprint->project->get_function_context(); - - LOG_DEBUG("blueprint={}", blueprint->id); - - VTRYRV( - auto count, - 0, - blueprint->project - ->get_db() - ->query_one("SELECT COUNT(*) FROM `blueprint_properties` WHERE `blueprint` = ?;", blueprint->id) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint property count: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint property count: {}", count); - - LOG_VERBOSE("successfully fetched blueprint property count"); - - return static_cast(count); + return ikarus::util::fetch_count( + blueprint, + ikarus::util::CountQueryData{ + .table_name = "blueprint_properties", + .select_field_name = "property", + .where_field_name = "blueprint", + .relation_desc = "properties" + } + ) + .unwrap_value_or(0); } void ikarus_blueprint_get_linked_entities( IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size ) { - LOG_VERBOSE("fetching blueprint linked entities"); - - LOG_DEBUG( - "project={}; blueprint={}; entities_out_size={}", - blueprint->project->get_path().c_str(), - blueprint->id, - entities_out_size + ikarus::util::fetch_multiple_buffered( + blueprint, + ikarus::util::MultipleBufferQueryData{ + .table_name = "entity_blueprint_links", + .select_field_name = "entity", + .where_field_name = "blueprint", + .relation_desc = "linked entities" + }, + entities_out, + entities_out_size, + [&](IkarusProject * project, [[maybe_unused]] IkarusFunctionContext * ctx, IkarusId id + ) -> cppbase::Result { return cppbase::ok(project->get_entity(id)); } ); - - auto * ctx = blueprint->project->get_function_context(); - - IkarusId ids[entities_out_size]; - - TRYRV( - , - blueprint->project - ->get_db() - ->query_many_buffered( - "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", - static_cast(ids), - entities_out_size, - blueprint->id - ) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint linked entity ids: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint linked entities: [{}]", fmt::join(ids, ids + entities_out_size, ", ")); - - for (size_t i = 0; i < entities_out_size; ++i) { - /// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - entities_out[i] = blueprint->project->get_entity(ids[i]); - } - - LOG_VERBOSE("successfully fetched blueprint linked entities"); } size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { - LOG_VERBOSE("fetching blueprint linked entity count"); - - LOG_DEBUG("project={}; blueprint={}", blueprint->project->get_path().c_str(), blueprint->id); - - auto * ctx = blueprint->project->get_function_context(); - - VTRYRV( - auto count, - 0, - blueprint->project - ->get_db() - ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?;", blueprint->id) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch blueprint linked entity count: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - LOG_DEBUG("blueprint linked entity count: {}", count); - - LOG_VERBOSE("successfully fetched blueprint linked entity count: {}", count); - - return static_cast(count); + return ikarus::util::fetch_count( + blueprint, + ikarus::util::CountQueryData{ + .table_name = "entity_blueprint_links", + .select_field_name = "entity", + .where_field_name = "blueprint", + .relation_desc = "linked entities" + } + ) + .unwrap_value_or(0); } IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint) { diff --git a/src/objects/util.hpp b/src/objects/util.hpp index 963701b..f192b7a 100644 --- a/src/objects/util.hpp +++ b/src/objects/util.hpp @@ -1,20 +1,43 @@ #pragma once +#include "util.hpp" + #include #include +#include #include #include #include -namespace util { -template F> -[[nodiscard]] cppbase::Result insert_object( - IkarusProject * project, FunctionContext * ctx, IkarusObjectType type, std::string_view name, F insert_function +namespace ikarus::util { + +struct EmptyNameError {}; + +COMPOUND_ERROR(InsertObjectError, EmptyNameError, sqlitecpp::TransactionError); + +template +[[nodiscard]] cppbase::Result, InsertObjectError> insert_object( + IkarusProject * project, + IkarusObjectType type, + std::string_view name, + InsertFunction insert_function, + ObjectFactory object_factory ) { - char const * object_type_str = ikarus_object_type_to_string(type); + auto const * object_type_str = ikarus_object_type_to_string(type); + + LOG_INFO("creating new {}", object_type_str); + + LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); + + auto * ctx = project->get_function_context(); + + if (cppbase::is_empty_or_blank(name)) { + ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + return cppbase::err(EmptyNameError{}); + } VTRY( auto const id, @@ -42,17 +65,23 @@ template F> }) ); - LOG_VERBOSE("successfully created blueprint"); + LOG_VERBOSE("successfully created {}", object_type_str); - return cppbase::ok(id); + return cppbase::ok(object_factory(id)); } -template -void delete_object(IkarusProject * project, FunctionContext * ctx, T * object) { - auto id = object->id; - auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(id)); +template + requires std::derived_from +void delete_object(IkarusProject * project, Object * object) { + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", id).on_error([&](auto const& err) { + LOG_INFO("deleting {}", object_type_str); + + LOG_DEBUG("project={}; {}={}", object_type_str, object->project->get_path().c_str(), object->id); + + auto * ctx = object->project->get_function_context(); + + TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const& err) { ctx->set_error( fmt::format("failed to delete {} from objects table: {}", object_type_str, err), true, @@ -73,8 +102,9 @@ struct SingleQueryData { std::string_view select_field_name; }; -template -cppbase::Result fetch_single_field(IkarusObject * object, SingleQueryData const& query_data) { +template + requires std::derived_from +cppbase::Result fetch_single_field(Object const * object, SingleQueryData const& query_data) { auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); LOG_VERBOSE("fetching property default value"); @@ -86,7 +116,7 @@ cppbase::Result fetch_single_field(IkarusObject VTRY( T value, object->project->get_db() - ->query_one( + ->template query_one( fmt::format("SELECT `{}` FROM `{}` WHERE `id` = ?", query_data.select_field_name, query_data.table_name), object->id ) @@ -107,45 +137,105 @@ struct MultipleBufferQueryData { std::string_view table_name; std::string_view select_field_name; std::string_view where_field_name; + std::string_view relation_desc; }; -template -cppbase::Result fetch_multiple_buffered( - IkarusObject * object, - MultipleBufferQueryData const& query_data, - std::string_view relation_desc, - T * buffer, - size_t buffer_size -) { +template + requires std::derived_from +void fetch_multiple_buffered( + Object const * object, MultipleBufferQueryData const& query_data, Mapped * mapped_buffer, size_t buffer_size, F transformer +) + requires cppbase:: + is_result_with_value_type_v> +{ + auto * ctx = object->project->get_function_context(); + auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - LOG_VERBOSE("fetching {} {}", object_type_str, relation_desc); + LOG_VERBOSE("fetching {} {}", object_type_str, query_data.relation_desc); LOG_VERBOSE("project={};{}={}", object->project->get_path().c_str(), object_type_str, object->id); - auto * ctx = object->project->get_function_context(); + Selected select_buffer[buffer_size]; - TRY(object->project->get_db() - ->query_many_buffered( + TRYRV( + , + object->project->get_db() + ->template query_many_buffered( fmt::format( "SELECT `{}` FROM `{}` WHERE `{}` = ?", query_data.select_field_name, query_data.table_name, query_data.where_field_name ), - buffer, + select_buffer, buffer_size, object->id ) .on_error([&](auto const& err) { ctx->set_error( - fmt::format("failed to fetch {} {} from database: {}", object_type_str, relation_desc, err), + fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.relation_desc, err), true, IkarusErrorInfo_Source_SubSystem, IkarusErrorInfo_Type_SubSystem_Database ); - })); + }) + ); - return cppbase::ok(); + LOG_DEBUG( + "{} {}: [{}]", object_type_str, query_data.relation_desc, fmt::join(select_buffer, select_buffer + buffer_size, ", ") + ); + + for (size_t i = 0; i < buffer_size; ++i) { + VTRYRV(mapped_buffer[i], , transformer(object->project, ctx, select_buffer[i])); + } } + +struct CountQueryData { + std::string_view table_name; + std::string_view select_field_name; + std::string_view where_field_name; + std::string_view relation_desc; +}; + +template + requires std::derived_from +cppbase::Result fetch_count(Object const * object, CountQueryData const& query_data) { + auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); + + LOG_VERBOSE("fetching {} {} count", object_type_str, query_data.relation_desc); + + auto * ctx = object->project->get_function_context(); + + LOG_DEBUG("{}={}", object_type_str, object->id); + + VTRY( + auto count, + object->project->get_db() + ->template query_one( + fmt::format( + "SELECT COUNT(`{}`) FROM `{}` WHERE `{}` = ?;", + query_data.select_field_name, + query_data.table_name, + query_data.where_field_name + ), + object->id + ) + .on_error([&](auto const& err) { + ctx->set_error( + fmt::format("failed to fetch {} {} count: {}", object_type_str, query_data.relation_desc, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + LOG_DEBUG("{} {} count: {}", object_type_str, query_data.relation_desc, count); + + LOG_VERBOSE("successfully fetched {} {} count", object_type_str, query_data.relation_desc); + + return cppbase::ok(static_cast(count)); +} + } diff --git a/src/persistence/function_context.cpp b/src/persistence/function_context.cpp index 96edddf..f056308 100644 --- a/src/persistence/function_context.cpp +++ b/src/persistence/function_context.cpp @@ -1,9 +1,9 @@ #include "function_context.hpp" -FunctionContext::FunctionContext(IkarusProject * project): +IkarusFunctionContext::IkarusFunctionContext(IkarusProject * project): _project{project} {} -FunctionContext::~FunctionContext() { +IkarusFunctionContext::~IkarusFunctionContext() { if (_project->_function_contexts.size() == 1) { if (_project->error_message_buffer.empty()) { _project->error_message_buffer.push_back('\0'); diff --git a/src/persistence/function_context.hpp b/src/persistence/function_context.hpp index 3dc64b3..769006b 100644 --- a/src/persistence/function_context.hpp +++ b/src/persistence/function_context.hpp @@ -12,17 +12,17 @@ #include -struct FunctionContext { +struct IkarusFunctionContext { public: - explicit FunctionContext(struct IkarusProject * project); + explicit IkarusFunctionContext(struct IkarusProject * project); - FunctionContext(FunctionContext const&) noexcept = default; - FunctionContext(FunctionContext&&) noexcept = default; + IkarusFunctionContext(IkarusFunctionContext const&) noexcept = default; + IkarusFunctionContext(IkarusFunctionContext&&) noexcept = default; - auto operator=(FunctionContext const&) noexcept -> FunctionContext& = default; - auto operator=(FunctionContext&&) noexcept -> FunctionContext& = default; + auto operator=(IkarusFunctionContext const&) noexcept -> IkarusFunctionContext& = default; + auto operator=(IkarusFunctionContext&&) noexcept -> IkarusFunctionContext& = default; - ~FunctionContext(); + ~IkarusFunctionContext(); public: template diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index 163bc93..bf1e51a 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -26,7 +26,7 @@ auto IkarusProject::get_db() const -> sqlitecpp::Connection const * { return _db.get(); } -auto IkarusProject::get_function_context() -> FunctionContext * { +auto IkarusProject::get_function_context() -> IkarusFunctionContext * { return &_function_contexts.emplace_back(this); } diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index 2af1fff..7d9d5b0 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -24,7 +24,7 @@ public: [[nodiscard]] auto get_db() const -> sqlitecpp::Connection const *; public: - [[nodiscard]] auto get_function_context() -> struct FunctionContext *; + [[nodiscard]] auto get_function_context() -> struct IkarusFunctionContext *; public: [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; @@ -54,7 +54,7 @@ private: } private: - friend struct FunctionContext; + friend struct IkarusFunctionContext; std::string _name; std::filesystem::path _path; @@ -67,5 +67,5 @@ private: std::unordered_map> _properties; std::unordered_map> _entities; - std::vector _function_contexts; + std::vector _function_contexts; }; From eab9bafe7bda30c032e17c0a82956b537ac92b1a Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 28 Dec 2023 16:52:05 +0100 Subject: [PATCH 115/166] change clang-format Signed-off-by: Folling --- .clang-format | 65 +++++++++++-------- include/ikarus/errors.h | 8 +-- include/ikarus/global.h | 4 +- include/ikarus/objects/blueprint.h | 10 ++- include/ikarus/objects/entity.h | 26 +++++--- include/ikarus/objects/object.h | 7 +- .../objects/properties/number_property.h | 13 ++-- include/ikarus/objects/properties/property.h | 4 +- .../ikarus/objects/properties/text_property.h | 13 ++-- .../objects/properties/toggle_property.h | 13 ++-- include/ikarus/persistence/project.h | 9 +-- include/ikarus/values/value.h | 4 +- src/objects/blueprint.cpp | 29 +++++---- src/objects/blueprint.hpp | 8 +-- src/objects/entity.hpp | 8 +-- src/objects/object.hpp | 8 +-- src/objects/properties/property.cpp | 61 +++++++---------- src/objects/properties/property.hpp | 17 +++-- src/objects/properties/property_source.hpp | 8 +-- src/objects/properties/toggle_property.cpp | 7 +- src/objects/util.hpp | 29 +++++---- src/persistence/function_context.hpp | 14 ++-- src/persistence/project.cpp | 5 +- src/persistence/project.hpp | 6 +- src/values/number_value.cpp | 2 +- src/values/number_value.hpp | 11 ++-- src/values/text_value.cpp | 2 +- src/values/text_value.hpp | 11 ++-- src/values/toggle_value.cpp | 2 +- src/values/toggle_value.hpp | 11 ++-- src/values/value.cpp | 11 ++-- src/values/value.hpp | 10 +-- src/values/value_base.hpp | 35 ++++------ 33 files changed, 217 insertions(+), 254 deletions(-) diff --git a/.clang-format b/.clang-format index 23c71e6..a600edb 100644 --- a/.clang-format +++ b/.clang-format @@ -15,8 +15,8 @@ AlignEscapedNewlines: Left AlignOperands: Align AlignTrailingComments: true -AllowAllArgumentsOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: true +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: true AllowShortEnumsOnASingleLine: true @@ -35,36 +35,38 @@ BinPackParameters: false BitFieldColonSpacing: Both BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false BeforeCatch: false BeforeElse: false BeforeLambdaBody: false BeforeWhile: false - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: MultiLine - AfterEnum: false - AfterExternBlock: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false IndentBraces: false SplitEmptyFunction: false SplitEmptyNamespace: false SplitEmptyRecord: false +BracedInitializerIndentWidth: 4 + +# BreakAdjacentStringLiterals: true BreakAfterAttributes: Never BreakBeforeBinaryOperators: None -BreakBeforeBraces: Attach +BreakBeforeBraces: Custom BreakBeforeConceptDeclarations: Always -# BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon BreakStringLiterals: false -ColumnLimit: 128 +ColumnLimit: 140 CompactNamespaces: false ConstructorInitializerIndentWidth: 4 @@ -77,7 +79,9 @@ DerivePointerAlignment: false EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: Always -FixNamespaceComments: false +ExperimentalAutoDetectBinPacking: true + +FixNamespaceComments: true IncludeBlocks: Regroup IncludeCategories: @@ -112,54 +116,59 @@ IndentAccessModifiers: false IndentCaseBlocks: false IndentCaseLabels: false IndentExternBlock: NoIndent -IndentGotoLabels: true +IndentGotoLabels: false IndentPPDirectives: None IndentRequiresClause: true IndentWidth: 4 IndentWrappedFunctionNames: false InsertBraces: true InsertNewlineAtEOF: true +InsertTrailingCommas: Wrapped -# InsertNewlineAtEOF: true -# IntegerLiteralSeparator: -# Binary: 0 -# Decimal: 3 -# Hex: -1 +IntegerLiteralSeparator: + Binary: -1 + Decimal: 3 + Hex: -1 +KeepEmptyLinesAtEOF: false KeepEmptyLinesAtTheStartOfBlocks: false LambdaBodyIndentation: Signature Language: Cpp -# LineEnding: LF +LineEnding: LF MaxEmptyLinesToKeep: 1 NamespaceIndentation: None +PPIndentWidth: -1 PackConstructorInitializers: Never PointerAlignment: Middle QualifierAlignment: Right # QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ] -ReferenceAlignment: Left +ReferenceAlignment: Middle ReflowComments: true -# RemoveSemicolon: true +RemoveBracesLLVM: false +RemoveParentheses: MultipleParentheses +RemoveSemicolon: true RequiresClausePosition: OwnLine -# RequiresExpressionIndentation: OuterScope +RequiresExpressionIndentation: OuterScope SeparateDefinitionBlocks: Always SortIncludes: CaseInsensitive -SortUsingDeclarations: true +SortUsingDeclarations: LexicographicNumeric SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: false - +SpaceAroundPointerQualifiers: Both SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: false SpaceBeforeInheritanceColon: true diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index 9dc876d..3218d74 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -15,10 +15,10 @@ IKARUS_BEGIN_HEADER /// \brief Delineates what caused an error. -/// \details First 2 bytes delineate the major type, next 2 bytes delineate the minor type, next 4 bytes delineate the detail -/// type. -/// \remark Note that this doesn't show responsibility. An error with source "SubSystem" could still be the fault of -/// libikarus. +/// \details First 2 bytes delineate the major type, next 2 bytes delineate the minor type, next 4 bytes delineate the +/// detail type. +/// \remark Note that this doesn't show responsibility. An error with source "SubSystem" could still be the +/// fault of libikarus. enum IkarusErrorInfo { /// \brief No error occurred. IkarusErrorInfo_Source_None = 0x0001000000000000, diff --git a/include/ikarus/global.h b/include/ikarus/global.h index bf9e5d5..4665863 100644 --- a/include/ikarus/global.h +++ b/include/ikarus/global.h @@ -11,8 +11,8 @@ IKARUS_BEGIN_HEADER -/// \brief Frees a pointer allocated by ikarus. Every pointer returned by a function must be freed using this function unless -/// explicitly stated otherwise. +/// \brief Frees a pointer allocated by ikarus. Every pointer returned by a function must be freed using this function +/// unless explicitly stated otherwise. IKA_API void ikarus_free(void * ptr); IKARUS_END_HEADER diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index ce83160..2163e6f 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -43,9 +43,8 @@ IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); /// \pre \li Must not be null. /// \param properties_out_size The size of the buffer. /// \see ikarus_blueprint_get_property_count -IKA_API void ikarus_blueprint_get_properties( - IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size -); +IKA_API void +ikarus_blueprint_get_properties(IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size); /// \brief Gets the number of properties of a blueprint. /// \param blueprint The blueprint to get the number of properties of. @@ -62,9 +61,8 @@ IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * bluep /// \pre \li Must not be null. /// \param entities_out_size The size of the buffer. /// \see ikarus_blueprint_get_linked_entity_count -IKA_API void ikarus_blueprint_get_linked_entities( - IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size -); +IKA_API void +ikarus_blueprint_get_linked_entities(IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size); /// \brief Gets the number of entities linked to a blueprint. /// \param blueprint The blueprint to get the number of linked entities of. diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 782452e..3e38e25 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -56,6 +56,14 @@ IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char /// \remark The entity must not be accessed after deletion. IKA_API void ikarus_entity_delete(IkarusEntity * entity); +/// \brief Checks if an entity is linked to a blueprint. +/// \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. +/// \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); /// \brief Links an entity to a blueprint. @@ -68,8 +76,8 @@ IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, s /// \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); -/// \brief Unlinks an entity from a blueprint. All values of the properties of the blueprint the entity is linked with will be -/// deleted. +/// \brief Unlinks an entity from a blueprint. All values of the properties of the blueprint the entity is linked with +/// will be deleted. /// \param entity The entity to unlink. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -103,12 +111,11 @@ IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity); /// \param properties_out The buffer to write the properties to. /// \pre \li Must not be null. /// \param properties_out_size The size of the buffer. -IKA_API void ikarus_entity_get_properties( - IkarusEntity const * entity, struct IkarusProperty ** properties_out, size_t properties_out_size -); +IKA_API void ikarus_entity_get_properties(IkarusEntity const * entity, struct IkarusProperty ** properties_out, size_t properties_out_size); /// \brief Gets the value of a property of an entity. -/// \details If the entity has never set the value of the property, the default value is returned (which may be undefined). +/// \details If the entity has never set the value of the property, the default value is returned (which may be +/// undefined). /// \param entity The entity to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -116,7 +123,8 @@ IKA_API void ikarus_entity_get_properties( /// \pre \li Must not be null. /// \pre \li Must exist. /// \return The value of the property or null if the entity does not have the property or an error occurs. -/// \remark Must be freed using #ikarus_free. +/// \remark Must be freed using +/// #ikarus_free. IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property); /// \brief Sets the value of a property of an entity. @@ -131,9 +139,7 @@ IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * /// \pre \li Must be of the same type as the property. /// \pre \li Must be valid for the property's settings. /// \remark If the entity does not have the property, this function fails. -IKA_API void ikarus_entity_set_value( - IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value -); +IKA_API void ikarus_entity_set_value(IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value); /// \brief Casts an entity to an object. /// \param entity The entity to cast. diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index abaa580..ba754c5 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -19,11 +19,8 @@ IKARUS_BEGIN_HEADER struct IkarusObject; /// \brief Compares two objects for equality. -/// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the same -/// object. -/// \param lhs The left hand side object. -/// \pre \li Must not be null. -/// \param rhs The right hand side object. +/// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the +/// same object. \param lhs The left hand side object. \pre \li Must not be null. \param rhs The right hand side object. /// \pre \li Must not be null. /// \return True if the objects are equal, false otherwise. IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs); diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 9b4fa4d..3108201 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -13,9 +13,8 @@ IKARUS_BEGIN_HEADER struct IkarusNumberProperty; -IKA_API IkarusNumberProperty * ikarus_number_property_create( - struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source -); +IKA_API IkarusNumberProperty * +ikarus_number_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); /// \brief Sets the default value for a number property. /// \param property The number property. @@ -30,11 +29,9 @@ IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(stru /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values -/// and other settings. -IKA_API void ikarus_number_property_set_default_value( - struct IkarusNumberProperty * property, struct IkarusNumberValue * default_value -); +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between +/// default values and other settings. +IKA_API void ikarus_number_property_set_default_value(struct IkarusNumberProperty * property, struct IkarusNumberValue * default_value); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index c0f37fa..ef28ca2 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -33,8 +33,8 @@ IKARUS_BEGIN_HEADER /// - May be undefined /// /// Additionally, each property has a default value. If no default value is provided, a sensible default is chosen. -/// Setting a default value that isn't valid for the property is an error. Changing settings so that the current default value -/// becomes invalid is valid but unsets the custom default value. +/// Setting a default value that isn't valid for the property is an error. Changing settings so that the current default +/// value becomes invalid is valid but unsets the custom default value. /// /// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. /// The latter allows you to specify an "unknown" value for a property. diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index 0e6a435..9c91c80 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -13,9 +13,8 @@ IKARUS_BEGIN_HEADER struct IkarusTextProperty; -IKA_API IkarusTextProperty * ikarus_text_property_create( - struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source -); +IKA_API IkarusTextProperty * +ikarus_text_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); /// \brief Sets the default value for a text property. /// \param property The text property. @@ -30,11 +29,9 @@ IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct I /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values -/// and other settings. -IKA_API void ikarus_text_property_set_default_value( - struct IkarusTextProperty * property, struct IkarusTextValue * default_value -); +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between +/// default values and other settings. +IKA_API void ikarus_text_property_set_default_value(struct IkarusTextProperty * property, struct IkarusTextValue * default_value); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index 0d67304..cfa51e3 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -13,9 +13,8 @@ IKARUS_BEGIN_HEADER struct IkarusToggleProperty; -IKA_API IkarusToggleProperty * ikarus_toggle_property_create( - struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source -); +IKA_API IkarusToggleProperty * +ikarus_toggle_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); /// \brief Sets the default value for a toggle property. /// \param property The toggle property. @@ -30,11 +29,9 @@ IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(stru /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between default values -/// and other settings. -IKA_API void ikarus_toggle_property_set_default_value( - struct IkarusToggleProperty * property, struct IkarusToggleValue * default_value -); +/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between +/// default values and other settings. +IKA_API void ikarus_toggle_property_set_default_value(struct IkarusToggleProperty * property, struct IkarusToggleValue * default_value); IKARUS_END_HEADER diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index a041946..7df0e45 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -144,9 +144,8 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project) /// \param blueprints_out The buffer to write the blueprints to. /// \pre \li Must not be null. /// \param blueprints_out_size The size of the buffer. -IKA_API void ikarus_project_get_blueprints( - IkarusProject const * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size -); +IKA_API void +ikarus_project_get_blueprints(IkarusProject const * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size); /// \brief Gets the entity root folder of a project. /// \param project The project to get the entity root folder of. @@ -170,9 +169,7 @@ IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project); /// \param entities_out The buffer to write the entities to. /// \pre \li Must not be null. /// \param entities_out_size The size of the buffer. -IKA_API void ikarus_project_get_entities( - IkarusProject const * project, struct IkarusEntity ** entities_out, size_t entities_out_size -); +IKA_API void ikarus_project_get_entities(IkarusProject const * project, struct IkarusEntity ** entities_out, size_t entities_out_size); IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index d33f87a..880c2eb 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -12,8 +12,8 @@ /// Each value may be undefined. \see IkarusProperty /// Values are stored as lists. If a property is "singular" then its value is a list of size 1. /// Values are typed, with types existing for each of the corresponding property types. -/// When setting values for a property the type must match the property type and the value must be valid under the property's -/// settings. \see PropertyType +/// When setting values for a property the type must match the property type and the value must be valid under the +/// property's settings. \see PropertyType /// @{ IKARUS_BEGIN_HEADER diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index af5fc08..c8bf1e5 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -31,7 +31,9 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { } void ikarus_blueprint_get_properties( - IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size + IkarusBlueprint const * blueprint, + struct IkarusProperty ** properties_out, + size_t properties_out_size ) { ikarus::util::fetch_multiple_buffered( blueprint, @@ -43,18 +45,15 @@ void ikarus_blueprint_get_properties( }, properties_out, properties_out_size, - [&](IkarusProject * project, IkarusFunctionContext * ctx, IkarusId id - ) -> cppbase::Result { - VTRY( - auto const type, IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch property {}'s type: {}", id, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); + [&](IkarusProject * project, IkarusFunctionContext * ctx, IkarusId id) -> cppbase::Result { + VTRY(auto const type, IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const & err) { + ctx->set_error( + fmt::format("failed to fetch property {}'s type: {}", id, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + })); return cppbase::ok(project->get_property(id, type)); } @@ -75,7 +74,9 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { } void ikarus_blueprint_get_linked_entities( - IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size + IkarusBlueprint const * blueprint, + struct IkarusEntity ** entities_out, + size_t entities_out_size ) { ikarus::util::fetch_multiple_buffered( blueprint, diff --git a/src/objects/blueprint.hpp b/src/objects/blueprint.hpp index 6fa6281..61aca64 100644 --- a/src/objects/blueprint.hpp +++ b/src/objects/blueprint.hpp @@ -5,11 +5,11 @@ struct IkarusBlueprint : IkarusObject { IkarusBlueprint(struct IkarusProject * project, IkarusId id); - IkarusBlueprint(IkarusBlueprint const&) = default; - IkarusBlueprint(IkarusBlueprint&&) = default; + IkarusBlueprint(IkarusBlueprint const &) = default; + IkarusBlueprint(IkarusBlueprint &&) = default; - IkarusBlueprint& operator=(IkarusBlueprint const&) = default; - IkarusBlueprint& operator=(IkarusBlueprint&&) = default; + IkarusBlueprint & operator=(IkarusBlueprint const &) = default; + IkarusBlueprint & operator=(IkarusBlueprint &&) = default; ~IkarusBlueprint() override = default; }; diff --git a/src/objects/entity.hpp b/src/objects/entity.hpp index f6d4f90..92cf4c6 100644 --- a/src/objects/entity.hpp +++ b/src/objects/entity.hpp @@ -6,11 +6,11 @@ struct IkarusEntity : IkarusObject { inline IkarusEntity(struct IkarusProject * project, IkarusId id): IkarusObject{project, id} {} - IkarusEntity(IkarusEntity const&) = default; - IkarusEntity(IkarusEntity&&) = default; + IkarusEntity(IkarusEntity const &) = default; + IkarusEntity(IkarusEntity &&) = default; - IkarusEntity& operator=(IkarusEntity const&) = default; - IkarusEntity& operator=(IkarusEntity&&) = default; + IkarusEntity & operator=(IkarusEntity const &) = default; + IkarusEntity & operator=(IkarusEntity &&) = default; ~IkarusEntity() override = default; }; diff --git a/src/objects/object.hpp b/src/objects/object.hpp index 5a94aa2..c51ddbb 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -10,11 +10,11 @@ struct IkarusObject { public: IkarusObject(struct IkarusProject * project, IkarusId id); - IkarusObject(IkarusObject const&) = default; - IkarusObject(IkarusObject&&) = default; + IkarusObject(IkarusObject const &) = default; + IkarusObject(IkarusObject &&) = default; - IkarusObject& operator=(IkarusObject const&) = default; - IkarusObject& operator=(IkarusObject&&) = default; + IkarusObject & operator=(IkarusObject const &) = default; + IkarusObject & operator=(IkarusObject &&) = default; virtual ~IkarusObject() = default; diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index 2abef0a..bea1a0d 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -14,17 +14,15 @@ IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): IkarusObject{project, id}, _data{data} {} -IkarusProperty::Data& IkarusProperty::get_data() { +IkarusProperty::Data & IkarusProperty::get_data() { return _data; } -IkarusProperty::Data const& IkarusProperty::get_data() const { +IkarusProperty::Data const & IkarusProperty::get_data() const { return _data; } -cppbase::Result IkarusProperty::get_property_type( - IkarusProject * project, IkarusId id -) { +cppbase::Result IkarusProperty::get_property_type(IkarusProject * project, IkarusId id) { LOG_DEBUG("fetching unboxed property type"); LOG_VERBOSE("project={};property={}", project->get_path().c_str(), id); @@ -33,16 +31,14 @@ cppbase::Result IkarusProperty: VTRY( auto const type, - project->get_db() - ->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to fetch unboxed property type: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) + project->get_db()->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id).on_error([ctx](auto const & err) { + ctx->set_error( + fmt::format("failed to fetch unboxed property type: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) ); return cppbase::ok(static_cast(type)); @@ -55,20 +51,14 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { auto * ctx = property->project->get_function_context(); - TRYRV( - , - property->project - ->get_db() - ->execute("DELETE FROM `objects` WHERE `id` = ?", property->id) - .on_error([ctx](auto const& err) { - ctx->set_error( - fmt::format("failed to delete property from objects table: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); + TRYRV(, property->project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([ctx](auto const & err) { + ctx->set_error( + fmt::format("failed to delete property from objects table: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + })); LOG_VERBOSE("property was successfully deleted from database, freeing"); @@ -80,8 +70,7 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { LOG_VERBOSE("fetching property type"); - return IkarusProperty::get_property_type(property->project, property->id) - .unwrap_value_or(IkarusPropertyType_Toggle); + return IkarusProperty::get_property_type(property->project, property->id).unwrap_value_or(IkarusPropertyType_Toggle); } IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { @@ -94,10 +83,9 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p VTRYRV( auto const source, nullptr, - property->project - ->get_db() + property->project->get_db() ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) - .on_error([ctx](auto const& err) { + .on_error([ctx](auto const & err) { ctx->set_error( fmt::format("failed to fetch property's source: {}", err), true, @@ -133,10 +121,9 @@ IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) VTRYRV( auto const value, nullptr, - property->project - ->get_db() + property->project->get_db() ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id) - .on_error([ctx](auto const& err) { + .on_error([ctx](auto const & err) { ctx->set_error( fmt::format("failed to fetch property's default value: {}", err), true, diff --git a/src/objects/properties/property.hpp b/src/objects/properties/property.hpp index 6acd377..efdb515 100644 --- a/src/objects/properties/property.hpp +++ b/src/objects/properties/property.hpp @@ -16,24 +16,23 @@ public: public: /// \brief Helper to fetch a type for a property that isn't yet wrapped in an object - [[nodiscard]] static cppbase::Result get_property_type( - struct IkarusProject * project, IkarusId id - ); + [[nodiscard]] static cppbase::Result + get_property_type(struct IkarusProject * project, IkarusId id); public: IkarusProperty(struct IkarusProject * project, IkarusId id, Data data); - IkarusProperty(IkarusProperty const&) = default; - IkarusProperty(IkarusProperty&&) = default; + IkarusProperty(IkarusProperty const &) = default; + IkarusProperty(IkarusProperty &&) = default; - IkarusProperty& operator=(IkarusProperty const&) = default; - IkarusProperty& operator=(IkarusProperty&&) = default; + IkarusProperty & operator=(IkarusProperty const &) = default; + IkarusProperty & operator=(IkarusProperty &&) = default; ~IkarusProperty() override = default; public: - [[nodiscard]] Data& get_data(); - [[nodiscard]] Data const& get_data() const; + [[nodiscard]] Data & get_data(); + [[nodiscard]] Data const & get_data() const; private: Data _data; diff --git a/src/objects/properties/property_source.hpp b/src/objects/properties/property_source.hpp index 0fda4ed..a98d664 100644 --- a/src/objects/properties/property_source.hpp +++ b/src/objects/properties/property_source.hpp @@ -11,11 +11,11 @@ public: public: explicit IkarusPropertySource(Data data); - IkarusPropertySource(IkarusPropertySource const&) = default; - IkarusPropertySource(IkarusPropertySource&&) = default; + IkarusPropertySource(IkarusPropertySource const &) = default; + IkarusPropertySource(IkarusPropertySource &&) = default; - IkarusPropertySource& operator=(IkarusPropertySource const&) = default; - IkarusPropertySource& operator=(IkarusPropertySource&&) = default; + IkarusPropertySource & operator=(IkarusPropertySource const &) = default; + IkarusPropertySource & operator=(IkarusPropertySource &&) = default; virtual ~IkarusPropertySource() = default; diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp index 70ec934..db5105b 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/objects/properties/toggle_property.cpp @@ -3,8 +3,5 @@ IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} -IkarusToggleProperty * ikarus_toggle_property_create( - struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source -) { - -} +IkarusToggleProperty * +ikarus_toggle_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source) {} diff --git a/src/objects/util.hpp b/src/objects/util.hpp index f192b7a..0ba4b37 100644 --- a/src/objects/util.hpp +++ b/src/objects/util.hpp @@ -55,7 +55,7 @@ template return cppbase::ok(id); }) - .on_error([&](auto const& err) { + .on_error([&](auto const & err) { ctx->set_error( fmt::format("unable to insert {} into database: {}", object_type_str, err), true, @@ -81,7 +81,7 @@ void delete_object(IkarusProject * project, Object * object) { auto * ctx = object->project->get_function_context(); - TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const& err) { + TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to delete {} from objects table: {}", object_type_str, err), true, @@ -104,7 +104,7 @@ struct SingleQueryData { template requires std::derived_from -cppbase::Result fetch_single_field(Object const * object, SingleQueryData const& query_data) { +cppbase::Result fetch_single_field(Object const * object, SingleQueryData const & query_data) { auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); LOG_VERBOSE("fetching property default value"); @@ -120,7 +120,7 @@ cppbase::Result fetch_single_field(Object const fmt::format("SELECT `{}` FROM `{}` WHERE `id` = ?", query_data.select_field_name, query_data.table_name), object->id ) - .on_error([&](auto const& err) { + .on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.select_field_name, err), true, @@ -143,10 +143,13 @@ struct MultipleBufferQueryData { template requires std::derived_from void fetch_multiple_buffered( - Object const * object, MultipleBufferQueryData const& query_data, Mapped * mapped_buffer, size_t buffer_size, F transformer + Object const * object, + MultipleBufferQueryData const & query_data, + Mapped * mapped_buffer, + size_t buffer_size, + F transformer ) - requires cppbase:: - is_result_with_value_type_v> + requires cppbase::is_result_with_value_type_v> { auto * ctx = object->project->get_function_context(); @@ -172,7 +175,7 @@ void fetch_multiple_buffered( buffer_size, object->id ) - .on_error([&](auto const& err) { + .on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.relation_desc, err), true, @@ -182,9 +185,7 @@ void fetch_multiple_buffered( }) ); - LOG_DEBUG( - "{} {}: [{}]", object_type_str, query_data.relation_desc, fmt::join(select_buffer, select_buffer + buffer_size, ", ") - ); + LOG_DEBUG("{} {}: [{}]", object_type_str, query_data.relation_desc, fmt::join(select_buffer, select_buffer + buffer_size, ", ")); for (size_t i = 0; i < buffer_size; ++i) { VTRYRV(mapped_buffer[i], , transformer(object->project, ctx, select_buffer[i])); @@ -200,7 +201,7 @@ struct CountQueryData { template requires std::derived_from -cppbase::Result fetch_count(Object const * object, CountQueryData const& query_data) { +cppbase::Result fetch_count(Object const * object, CountQueryData const & query_data) { auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); LOG_VERBOSE("fetching {} {} count", object_type_str, query_data.relation_desc); @@ -221,7 +222,7 @@ cppbase::Result fetch_count(Object const ), object->id ) - .on_error([&](auto const& err) { + .on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to fetch {} {} count: {}", object_type_str, query_data.relation_desc, err), true, @@ -238,4 +239,4 @@ cppbase::Result fetch_count(Object const return cppbase::ok(static_cast(count)); } -} +} // namespace ikarus::util diff --git a/src/persistence/function_context.hpp b/src/persistence/function_context.hpp index 769006b..663ff2d 100644 --- a/src/persistence/function_context.hpp +++ b/src/persistence/function_context.hpp @@ -16,11 +16,11 @@ struct IkarusFunctionContext { public: explicit IkarusFunctionContext(struct IkarusProject * project); - IkarusFunctionContext(IkarusFunctionContext const&) noexcept = default; - IkarusFunctionContext(IkarusFunctionContext&&) noexcept = default; + IkarusFunctionContext(IkarusFunctionContext const &) noexcept = default; + IkarusFunctionContext(IkarusFunctionContext &&) noexcept = default; - auto operator=(IkarusFunctionContext const&) noexcept -> IkarusFunctionContext& = default; - auto operator=(IkarusFunctionContext&&) noexcept -> IkarusFunctionContext& = default; + auto operator=(IkarusFunctionContext const &) noexcept -> IkarusFunctionContext & = default; + auto operator=(IkarusFunctionContext &&) noexcept -> IkarusFunctionContext & = default; ~IkarusFunctionContext(); @@ -40,11 +40,7 @@ public: _project->error_infos = {infos...}; if (log_error) { - LOG_ERROR( - "Error({}): {}", - fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), - error_message - ); + LOG_ERROR("Error({}): {}", fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), error_message); } } diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index bf1e51a..0be9991 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -14,7 +14,7 @@ auto IkarusProject::get_name() const -> std::string_view { return _name; } -auto IkarusProject::get_path() const -> std::filesystem::path const& { +auto IkarusProject::get_path() const -> std::filesystem::path const & { return _path; } @@ -55,8 +55,7 @@ auto IkarusProject::get_property(IkarusId id, IkarusPropertyType type) -> Ikarus return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); case IkarusPropertyType_Number: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); - case IkarusPropertyType_Text: - return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); + case IkarusPropertyType_Text: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); } } diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index 7d9d5b0..2359f2e 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -18,7 +18,7 @@ struct IkarusProject { public: [[nodiscard]] auto get_name() const -> std::string_view; - [[nodiscard]] auto get_path() const -> std::filesystem::path const&; + [[nodiscard]] auto get_path() const -> std::filesystem::path const &; [[nodiscard]] auto get_db() -> sqlitecpp::Connection *; [[nodiscard]] auto get_db() const -> sqlitecpp::Connection const *; @@ -38,7 +38,7 @@ public: private: template - [[nodiscard]] T * get_cached_object(IkarusId id, auto& cache) { + [[nodiscard]] T * get_cached_object(IkarusId id, auto & cache) { auto const iter = cache.find(id); if (iter == cache.cend()) { @@ -49,7 +49,7 @@ private: } template - void remove_cached_object(T * object, std::unordered_map>& cache) { + void remove_cached_object(T * object, std::unordered_map> & cache) { cache.erase(object->id); } diff --git a/src/values/number_value.cpp b/src/values/number_value.cpp index b408925..c339149 100644 --- a/src/values/number_value.cpp +++ b/src/values/number_value.cpp @@ -45,7 +45,7 @@ void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined } char const * ikarus_number_value_to_string(IkarusNumberValue const * value) { - return ikarus_value_base_to_string(value, [](auto const& value) { return value; }); + return ikarus_value_base_to_string(value, [](auto const & value) { return value; }); } bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs) { diff --git a/src/values/number_value.hpp b/src/values/number_value.hpp index 1421ca3..8644d3e 100644 --- a/src/values/number_value.hpp +++ b/src/values/number_value.hpp @@ -12,15 +12,14 @@ public: public: explicit IkarusNumberValue(); - IkarusNumberValue(IkarusNumberValue const&) = default; - IkarusNumberValue(IkarusNumberValue&&) = default; + IkarusNumberValue(IkarusNumberValue const &) = default; + IkarusNumberValue(IkarusNumberValue &&) = default; - IkarusNumberValue& operator=(IkarusNumberValue const&) = default; - IkarusNumberValue& operator=(IkarusNumberValue&&) = default; + IkarusNumberValue & operator=(IkarusNumberValue const &) = default; + IkarusNumberValue & operator=(IkarusNumberValue &&) = default; ~IkarusNumberValue() override = default; public: - boost::variant2::variant> data{ - }; + boost::variant2::variant> data{}; }; diff --git a/src/values/text_value.cpp b/src/values/text_value.cpp index ff09770..64006ac 100644 --- a/src/values/text_value.cpp +++ b/src/values/text_value.cpp @@ -46,7 +46,7 @@ void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined) { } char const * ikarus_text_value_to_string(IkarusTextValue const * value) { - return ikarus_value_base_to_string(value, [](auto const& value) { return value; }); + return ikarus_value_base_to_string(value, [](auto const & value) { return value; }); } bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs) { diff --git a/src/values/text_value.hpp b/src/values/text_value.hpp index 117e175..40d3945 100644 --- a/src/values/text_value.hpp +++ b/src/values/text_value.hpp @@ -11,15 +11,14 @@ public: public: explicit IkarusTextValue(); - IkarusTextValue(IkarusTextValue const&) = default; - IkarusTextValue(IkarusTextValue&&) = default; + IkarusTextValue(IkarusTextValue const &) = default; + IkarusTextValue(IkarusTextValue &&) = default; - IkarusTextValue& operator=(IkarusTextValue const&) = default; - IkarusTextValue& operator=(IkarusTextValue&&) = default; + IkarusTextValue & operator=(IkarusTextValue const &) = default; + IkarusTextValue & operator=(IkarusTextValue &&) = default; ~IkarusTextValue() override = default; public: - boost::variant2::variant> data{ - }; + boost::variant2::variant> data{}; }; diff --git a/src/values/toggle_value.cpp b/src/values/toggle_value.cpp index c5ced27..dabbe00 100644 --- a/src/values/toggle_value.cpp +++ b/src/values/toggle_value.cpp @@ -46,7 +46,7 @@ void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined } char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value) { - return ikarus_value_base_to_string(value, [](auto const& value) { return value ? "✓" : "✗"; }); + return ikarus_value_base_to_string(value, [](auto const & value) { return value ? "✓" : "✗"; }); } bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs) { diff --git a/src/values/toggle_value.hpp b/src/values/toggle_value.hpp index 7e2240f..20a5dd6 100644 --- a/src/values/toggle_value.hpp +++ b/src/values/toggle_value.hpp @@ -11,15 +11,14 @@ public: public: explicit IkarusToggleValue(); - IkarusToggleValue(IkarusToggleValue const&) = default; - IkarusToggleValue(IkarusToggleValue&&) = default; + IkarusToggleValue(IkarusToggleValue const &) = default; + IkarusToggleValue(IkarusToggleValue &&) = default; - IkarusToggleValue& operator=(IkarusToggleValue const&) = default; - IkarusToggleValue& operator=(IkarusToggleValue&&) = default; + IkarusToggleValue & operator=(IkarusToggleValue const &) = default; + IkarusToggleValue & operator=(IkarusToggleValue &&) = default; ~IkarusToggleValue() override = default; public: - boost::variant2::variant> data{ - }; + boost::variant2::variant> data{}; }; diff --git a/src/values/value.cpp b/src/values/value.cpp index 2cffcaa..cf9a270 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -19,7 +19,7 @@ IkarusValue::IkarusValue(Data data): data(data) {} -cppbase::Result IkarusValue::from_json(boost::json::value const& json) { +cppbase::Result IkarusValue::from_json(boost::json::value const & json) { if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); } else { @@ -43,8 +43,9 @@ cppbase::Result IkarusValue::from_jso ret = new T{}; ret->data = boost::variant2::monostate{}; } else { - auto res = boost::json::try_value_to< - boost::container::small_vector>(*data); + auto res = + boost::json::try_value_to>(*data + ); if (res.has_error()) { return cppbase::err(FromJsonError{}); @@ -88,8 +89,8 @@ boost::json::value IkarusValue::to_json() const { [](T const * value) -> boost::json::value { return boost::variant2::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); } + []([[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 ); diff --git a/src/values/value.hpp b/src/values/value.hpp index 20b4717..adc27ca 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -13,18 +13,18 @@ public: public: explicit IkarusValue(Data data); - IkarusValue(IkarusValue const&) = default; - IkarusValue(IkarusValue&&) noexcept = default; + IkarusValue(IkarusValue const &) = default; + IkarusValue(IkarusValue &&) noexcept = default; - IkarusValue& operator=(IkarusValue const&) = default; - IkarusValue& operator=(IkarusValue&&) noexcept = default; + IkarusValue & operator=(IkarusValue const &) = default; + IkarusValue & operator=(IkarusValue &&) noexcept = default; virtual ~IkarusValue() = default; public: struct FromJsonError {}; - [[nodiscard]] static cppbase::Result from_json(boost::json::value const& json); + [[nodiscard]] static cppbase::Result from_json(boost::json::value const & json); [[nodiscard]] boost::json::value to_json() const; public: diff --git a/src/values/value_base.hpp b/src/values/value_base.hpp index 46cf457..d4e6e25 100644 --- a/src/values/value_base.hpp +++ b/src/values/value_base.hpp @@ -2,16 +2,14 @@ #include -#include - #include +#include + template typename V::DataType const * ikarus_value_base_get(V * value, size_t idx) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { return &(*data)[idx]; } @@ -22,9 +20,7 @@ typename V::DataType const * ikarus_value_base_get(V * value, size_t idx) { template size_t ikarus_value_base_get_size(V const * value) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { return data->size(); } @@ -35,9 +31,7 @@ size_t ikarus_value_base_get_size(V const * value) { template void ikarus_value_base_set(V * value, size_t idx, typename V::DataType new_data) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { (*data)[idx] = new_data; } @@ -46,9 +40,7 @@ void ikarus_value_base_set(V * value, size_t idx, typename V::DataType new_data) template void ikarus_value_base_remove(V * value, size_t idx) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { data->erase(data->begin() + idx); } @@ -57,9 +49,7 @@ void ikarus_value_base_remove(V * value, size_t idx) { template void ikarus_value_base_insert(V * value, size_t idx, typename V::DataType new_data) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { data->insert(data->begin() + idx, new_data); } @@ -68,9 +58,7 @@ void ikarus_value_base_insert(V * value, size_t idx, typename V::DataType new_da template void ikarus_value_base_clear(V * value) { if (auto * data = - boost::variant2::get_if>( - &value->data - ); + boost::variant2::get_if>(&value->data); data != nullptr) { data->clear(); } @@ -93,10 +81,9 @@ void ikarus_value_base_set_undefined(V * value, bool undefined) { template F> char const * ikarus_value_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 * { + cppbase::overloaded{ + [](boost::variant2::monostate const &) -> char const * { return nullptr; }, + [&transformer](auto const & data) -> char const * { auto buffer = fmt::memory_buffer{}; fmt::format_to( From 41f00bc8712d799d14f892b5a6519213f66b8c83 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 28 Dec 2023 23:42:43 +0100 Subject: [PATCH 116/166] remove logging statements Signed-off-by: Folling --- clang-format.txt | 192 ++++++++++++++++++++++++++++ src/objects/blueprint.cpp | 2 +- src/objects/entity.cpp | 40 ++++-- src/objects/properties/property.cpp | 22 ---- src/objects/util.hpp | 77 ++++++----- vendor/sqlitecpp | 2 +- 6 files changed, 260 insertions(+), 75 deletions(-) create mode 100644 clang-format.txt diff --git a/clang-format.txt b/clang-format.txt new file mode 100644 index 0000000..a600edb --- /dev/null +++ b/clang-format.txt @@ -0,0 +1,192 @@ +BasedOnStyle: Google + +AccessModifierOffset: -4 + +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: + Enabled: false +AlignConsecutiveBitFields: + Enabled: false +AlignConsecutiveDeclarations: + Enabled: false +AlignConsecutiveMacros: AcrossEmptyLines +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true + +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true + +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes + +BinPackArguments: false +BinPackParameters: false + +BitFieldColonSpacing: Both + +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyNamespace: false + SplitEmptyRecord: false + +BracedInitializerIndentWidth: 4 + +# BreakAdjacentStringLiterals: true +BreakAfterAttributes: Never +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeConceptDeclarations: Always +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: false + +ColumnLimit: 140 + +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 + +Cpp11BracedListStyle: true + +DerivePointerAlignment: false + +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: Always + +ExperimentalAutoDetectBinPacking: true + +FixNamespaceComments: true + +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^".+\.(h|hpp)"$' + Priority: 1 + - Regex: '^<[a-z0-9_]+\.h>$' + Priority: 2 + - Regex: '^<[a-z0-9_]+>$' + Priority: 3 + - Regex: '^$' + Priority: 4 + - Regex: '^$' + Priority: 5 + - Regex: '^$' + Priority: 6 + - Regex: '^$' + Priority: 7 + - Regex: '^$' + Priority: 8 + - Regex: '^$' + Priority: 9 + - Regex: '^$' + Priority: 10 + - Regex: '^$' + Priority: 11 + - Regex: '^$' + Priority: 12 + - Regex: '^$' + Priority: 13 + +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: NoIndent +IndentGotoLabels: false +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: true +InsertNewlineAtEOF: true +InsertTrailingCommas: Wrapped + +IntegerLiteralSeparator: + Binary: -1 + Decimal: 3 + Hex: -1 + +KeepEmptyLinesAtEOF: false +KeepEmptyLinesAtTheStartOfBlocks: false + +LambdaBodyIndentation: Signature +Language: Cpp + +LineEnding: LF + +MaxEmptyLinesToKeep: 1 + +NamespaceIndentation: None + +PPIndentWidth: -1 +PackConstructorInitializers: Never + +PointerAlignment: Middle +QualifierAlignment: Right +# QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ] +ReferenceAlignment: Middle + +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: MultipleParentheses +RemoveSemicolon: true + +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope + +SeparateDefinitionBlocks: Always + +SortIncludes: CaseInsensitive +SortUsingDeclarations: LexicographicNumeric + +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: Both +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: 1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++20 + +TabWidth: 4 +UseTab: Never diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index c8bf1e5..c15c7bc 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -27,7 +27,7 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c } void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { - ikarus::util::delete_object(blueprint->project, blueprint); + ikarus::util::delete_object(blueprint); } void ikarus_blueprint_get_properties( diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp index ce5d853..80d047e 100644 --- a/src/objects/entity.cpp +++ b/src/objects/entity.cpp @@ -2,20 +2,36 @@ #include +#include +#include #include #include IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name) { - LOG_INFO("creating new entity"); - - LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); - - auto * ctx = project->get_function_context(); - - if (cppbase::is_empty_or_blank(name)) { - ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return nullptr; - } - - // TODO + return ikarus::util::insert_object( + project, + IkarusObjectType_Entity, + name, + [](auto * db, IkarusId id) { return db->execute("INSERT INTO `entities`(`id`) VALUES(?)", id); }, + [project](IkarusId id) { return project->get_entity(id); } + ).unwrap_value_or(nullptr); } + +void ikarus_entity_delete(IkarusEntity * entity) { + ikarus::util::delete_object(entity); +} + +bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint) { + return ikarus::util::check_exists( + entity, + ikarus::util::ExistsQueryData{ + .table_name = "entity_blueprint_links", + .where_field_name = "blueprint", + .where_field_value = blueprint->id, + .relation_desc = "linked blueprints" + } + ) + .unwrap_value_or(false); +} + +bool ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint) {} diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index bea1a0d..d28275d 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -23,10 +23,6 @@ IkarusProperty::Data const & IkarusProperty::get_data() const { } cppbase::Result IkarusProperty::get_property_type(IkarusProject * project, IkarusId id) { - LOG_DEBUG("fetching unboxed property type"); - - LOG_VERBOSE("project={};property={}", project->get_path().c_str(), id); - auto * ctx = project->get_function_context(); VTRY( @@ -45,10 +41,6 @@ cppbase::Result IkarusProperty: } IKA_API void ikarus_property_delete(IkarusProperty * property) { - LOG_INFO("deleting property"); - - LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->project->get_function_context(); TRYRV(, property->project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([ctx](auto const & err) { @@ -60,24 +52,14 @@ IKA_API void ikarus_property_delete(IkarusProperty * property) { ); })); - LOG_VERBOSE("property was successfully deleted from database, freeing"); - property->project->uncache(property); - - LOG_VERBOSE("successfully deleted property"); } IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { - LOG_VERBOSE("fetching property type"); - return IkarusProperty::get_property_type(property->project, property->id).unwrap_value_or(IkarusPropertyType_Toggle); } IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { - LOG_VERBOSE("fetching property source"); - - LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->project->get_function_context(); VTRYRV( @@ -112,10 +94,6 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p } IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) { - LOG_VERBOSE("fetching property default value"); - - LOG_VERBOSE("project={};property={}", property->project->get_path().c_str(), property->id); - auto * ctx = property->project->get_function_context(); VTRYRV( diff --git a/src/objects/util.hpp b/src/objects/util.hpp index 0ba4b37..2779478 100644 --- a/src/objects/util.hpp +++ b/src/objects/util.hpp @@ -28,10 +28,6 @@ template ) { auto const * object_type_str = ikarus_object_type_to_string(type); - LOG_INFO("creating new {}", object_type_str); - - LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name); - auto * ctx = project->get_function_context(); if (cppbase::is_empty_or_blank(name)) { @@ -43,14 +39,10 @@ template auto const id, project->get_db() ->transact([&](auto * db) -> cppbase::Result { - LOG_VERBOSE("creating {} in objects table", object_type_str); - TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(type), name)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - LOG_DEBUG("{} is {}", object_type_str, id); - TRY(insert_function(db, id)); return cppbase::ok(id); @@ -65,23 +57,17 @@ template }) ); - LOG_VERBOSE("successfully created {}", object_type_str); - return cppbase::ok(object_factory(id)); } template requires std::derived_from -void delete_object(IkarusProject * project, Object * object) { +void delete_object(Object * object) { auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - LOG_INFO("deleting {}", object_type_str); - - LOG_DEBUG("project={}; {}={}", object_type_str, object->project->get_path().c_str(), object->id); - auto * ctx = object->project->get_function_context(); - TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const & err) { + TRYRV(, object->project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const & err) { ctx->set_error( fmt::format("failed to delete {} from objects table: {}", object_type_str, err), true, @@ -90,11 +76,7 @@ void delete_object(IkarusProject * project, Object * object) { ); })); - LOG_VERBOSE("{} was successfully deleted from database, freeing", object_type_str); - - project->uncache(object); - - LOG_VERBOSE("successfully deleted {}", object_type_str); + object->project->uncache(object); } struct SingleQueryData { @@ -107,10 +89,6 @@ template cppbase::Result fetch_single_field(Object const * object, SingleQueryData const & query_data) { auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - LOG_VERBOSE("fetching property default value"); - - LOG_VERBOSE("project={};property={}", object->project->get_path().c_str(), object->id); - auto * ctx = object->project->get_function_context(); VTRY( @@ -155,10 +133,6 @@ void fetch_multiple_buffered( auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - LOG_VERBOSE("fetching {} {}", object_type_str, query_data.relation_desc); - - LOG_VERBOSE("project={};{}={}", object->project->get_path().c_str(), object_type_str, object->id); - Selected select_buffer[buffer_size]; TRYRV( @@ -185,8 +159,6 @@ void fetch_multiple_buffered( }) ); - LOG_DEBUG("{} {}: [{}]", object_type_str, query_data.relation_desc, fmt::join(select_buffer, select_buffer + buffer_size, ", ")); - for (size_t i = 0; i < buffer_size; ++i) { VTRYRV(mapped_buffer[i], , transformer(object->project, ctx, select_buffer[i])); } @@ -204,12 +176,8 @@ template cppbase::Result fetch_count(Object const * object, CountQueryData const & query_data) { auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - LOG_VERBOSE("fetching {} {} count", object_type_str, query_data.relation_desc); - auto * ctx = object->project->get_function_context(); - LOG_DEBUG("{}={}", object_type_str, object->id); - VTRY( auto count, object->project->get_db() @@ -232,11 +200,42 @@ cppbase::Result fetch_count(Object const }) ); - LOG_DEBUG("{} {} count: {}", object_type_str, query_data.relation_desc, count); - - LOG_VERBOSE("successfully fetched {} {} count", object_type_str, query_data.relation_desc); - return cppbase::ok(static_cast(count)); } +template +struct ExistsQueryData { + std::string_view table_name; + std::string_view where_field_name; + T where_field_value; + std::string_view relation_desc; +}; + +template + requires std::derived_from +cppbase::Result check_exists(Object const * object, ExistsQueryData const & query_data) { + auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); + + auto * ctx = object->project->get_function_context(); + + VTRY( + auto exists, + object->project->get_db() + ->template query_one( + fmt::format("SELECT EXISTS(SELECT 1 FROM `{}` WHERE `{}` = ?);", query_data.table_name, query_data.where_field_name), + query_data.where_field_value + ) + .on_error([&](auto const & err) { + ctx->set_error( + fmt::format("failed to check whether {} {} exists: {}", object_type_str, query_data.relation_desc, err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); + + return cppbase::ok(static_cast(exists)); +} + } // namespace ikarus::util diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 2a93b8b..00a1afc 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 2a93b8b1a8be03a9a9c4c72956b1111299de0ddd +Subproject commit 00a1afcc5f564f562c436f1ddfa4f44bb6489b17 From 70f1fe7de073eeb16fd28bac7076299d5f21bdde Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 2 Jan 2024 15:14:39 +0100 Subject: [PATCH 117/166] change error system & function signatures Signed-off-by: Folling --- .clang-format | 2 +- .clang-tidy | 2 +- README.md | 2 +- include/ikarus/errors.h | 142 ++++++++--- include/ikarus/id.h | 6 +- include/ikarus/objects/blueprint.h | 36 ++- include/ikarus/objects/entity.h | 53 ++-- include/ikarus/objects/object.h | 16 +- include/ikarus/objects/object_type.h | 4 +- .../objects/properties/number_property.h | 35 ++- include/ikarus/objects/properties/property.h | 25 +- .../objects/properties/property_source.h | 15 +- .../ikarus/objects/properties/text_property.h | 34 ++- .../objects/properties/toggle_property.h | 33 ++- include/ikarus/persistence/project.h | 96 +++---- include/ikarus/values/number_value.h | 47 ++-- include/ikarus/values/text_value.h | 53 ++-- include/ikarus/values/toggle_value.h | 44 ++-- include/ikarus/values/value.h | 10 +- src/errors.cpp | 81 ++++-- src/objects/blueprint.cpp | 132 +++++----- src/objects/entity.cpp | 1 - src/objects/properties/property.cpp | 46 ++-- src/objects/util.hpp | 241 ------------------ src/persistence/function_context.cpp | 18 -- src/persistence/function_context.hpp | 49 ---- src/persistence/project.cpp | 27 +- src/persistence/project.hpp | 34 +-- 28 files changed, 633 insertions(+), 651 deletions(-) delete mode 100644 src/objects/util.hpp delete mode 100644 src/persistence/function_context.cpp delete mode 100644 src/persistence/function_context.hpp diff --git a/.clang-format b/.clang-format index a600edb..7d8a46d 100644 --- a/.clang-format +++ b/.clang-format @@ -67,7 +67,7 @@ BreakInheritanceList: AfterColon BreakStringLiterals: false ColumnLimit: 140 - +CommentPragmas: '^\\.+' CompactNamespaces: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 diff --git a/.clang-tidy b/.clang-tidy index 7262427..c2cf005 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,7 +1,7 @@ Checks: >- -*, bugprone-*, -bugprone-lambda-function-name, - cppcoreguidelines-*, -cppcoreguidelines-owning-memory, + cppcoreguidelines-*, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes, clang-analyzer-*, google-*, -google-readability-todo, modernize-*, -modernize-use-trailing-return-type, diff --git a/README.md b/README.md index 57b6325..67c46f2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A snapshot if you will. One must not rely on it representing the actual state of the project at any given time. The data is simply copied from the underlying data sources and returned to the caller. -No mechanisms are provided to avoid race conditions. LibIkarus itself should only be used in a single-threaded context. +No mechanisms are provided to avoid race conditions. libikarus itself should only be used in a single-threaded context. However, nothing breaks if you do use it in a multithreaded context, that is, libikarus is threadsafe. You just cannot rely on the data being consistent. This goes especially for inter-process access to the same project. \ No newline at end of file diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index 3218d74..92b16cf 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -7,57 +7,111 @@ /// \addtogroup errors Errors /// \brief Error handling within libikarus -/// \details Errors are stored for each project, akin to the errno handling in C. -/// We store multiple pieces of information about the error occurring. For more information see -/// #ikarus_project_get_error_message. +/// \details Functions in Ikarus may fail, in which case they have an out parameter for the error. +/// Upon erring the function will store relevant information about the error in the out parameter. +/// If the out parameter is null nothing will be stored. This is not recommended as it essentially ignores errors. +/// For the sake of simplicity we have avoided mechanisms that "force" clients to handle errors. +/// Note that Ikarus does not check for null pointers. Passing null pointers to functions that do not explicitly state that they accept null +/// pointers is undefined behaviour. This decision is done for the sake of brevity and readability. `project_get_name(project)` is also +/// synonymous to `project->get_name()` in OOP languages, which shares the same semantics. /// @{ IKARUS_BEGIN_HEADER /// \brief Delineates what caused an error. -/// \details First 2 bytes delineate the major type, next 2 bytes delineate the minor type, next 4 bytes delineate the -/// detail type. /// \remark Note that this doesn't show responsibility. An error with source "SubSystem" could still be the -/// fault of libikarus. +/// fault of libikarus, it just indicates where the error occurred. enum IkarusErrorInfo { /// \brief No error occurred. - IkarusErrorInfo_Source_None = 0x0001000000000000, + IkarusErrorInfo_None = 0x0, /// \brief The error was caused by the client. - IkarusErrorInfo_Source_Client = 0x0001000000000001, - /// \brief The error was caused by a sub-system of libikarus. - IkarusErrorInfo_Source_SubSystem = 0x0001000000000002, + IkarusErrorInfo_Client = 0x10100000, + /// \brief The error was caused by a dependency (e.g. boost) of libikarus. + IkarusErrorInfo_Dependency = 0x10200000, + /// \brief The error was caused by the filesystem. + IkarusErrorInfo_Filesystem = 0x10300000, + /// \brief The error was caused by the database. + IkarusErrorInfo_Database = 0x10400000, + /// \brief The error was caused by the underlying OS. + IkarusErrorInfo_OS = 0x10500000, /// \brief The error was caused by libikarus itself. - IkarusErrorInfo_Source_LibIkarus = 0x0001000000000003, - /// \brief The error was caused by an unknown source. - IkarusErrorInfo_Source_Unknown = 0x00010000FFFFFFFF, - /// \brief No error occurred. - IkarusErrorInfo_Type_None = 0x0002000000000000, - /// \brief The user misused the API. + IkarusErrorInfo_LibIkarus = 0x10600000, + + /// \brief The client misused the API. /// Example: Accessing a resource that does not exist. - IkarusErrorInfo_Type_Client_Misuse = 0x0002000100000001, - /// \brief The user provided invalid input. - /// Example: Passing null for a pointer that must not be null. - IkarusErrorInfo_Type_Client_Input = 0x0002000100000002, - /// \brief An error occurred while interacting with a dependency from ikarus. - /// Example: An error occurred in the underlying OS library. - IkarusErrorInfo_Type_SubSystem_Dependency = 0x0002000200000001, - /// \brief An error occurred while interacting with the database. - /// Example: An error occurred while executing a query. - IkarusErrorInfo_Type_SubSystem_Database = 0x0002000200000002, - /// \brief An error occurred while interacting with the filesystem. - /// Example: An error occurred while reading a file. - IkarusErrorInfo_Type_SubSystem_Filesystem = 0x0002000200000003, + IkarusErrorInfo_Client_Misuse = 0x10100001, + /// \brief The client provided a null value for a parameter that must not be null. + /// Example: Passing null for `ikarus_project_get_name` + IkarusErrorInfo_Client_InvalidNull = 0x10100002, + /// \brief The client provided an index that was out of bounds for some array. + /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3. + IkarusErrorInfo_Client_IndexOutOfBounds = 0x10100003, + /// \brief The client provided a numeric value that was out of bounds + /// Example: Passing the value 2^32 to an i32 (might be passed as a string). + IkarusErrorInfo_Client_ValueOutOfBounds = 0x10100004, + /// \brief The client provided invalid input that doesn't fit in any of the other categories. + /// Example: Passing an empty/blank string for a string that must be non-empty/-blank. + IkarusErrorInfo_Client_InvalidInput = 0x10100005, + /// \brief The client provided valid data in an invalid format. + /// Example: Passing a malformed JSON string. + IkarusErrorInfo_Client_InvalidFormat = 0x10100006, + /// \brief The client violated a constraint. + /// \details This error is most likely caused by endusers. + /// Example: A user tries to set the age of a character to an value outside of their specified range. + IkarusErrorInfo_Client_ConstraintViolated = 0x10100007, + + /// \brief A file was not found. + IkarusErrorInfo_Filesystem_NotFound = 0x10300001, + /// \brief A file or directory already exists. + IkarusErrorInfo_Filesystem_AlreadyExists = 0x10300002, + /// \brief Missing permissions to access a file or directory. + IkarusErrorInfo_Filesystem_MissingPermissions = 0x10300003, + /// \brief Insufficient space to perform an operation. + IkarusErrorInfo_Filesystem_InsufficientSpace = 0x10300004, + /// \brief A path is invalid. + IkarusErrorInfo_Filesystem_InvalidPath = 0x10300005, + + /// \brief A database connection failed. + IkarusErrorInfo_Database_ConnectionFailed = 0x10400001, + /// \brief A database query failed. + IkarusErrorInfo_Database_QueryFailed = 0x10400002, + /// \brief A database migration failed. + IkarusErrorInfo_Database_MigrationFailed = 0x10400003, + /// \brief A database is in an invalid state. This indicates a corrupt project. + /// Example: An entity is linked to a non-existant blueprint. + IkarusErrorInfo_Database_InvalidState = 0x10400004, + + /// \brief A system call failed. + IkarusErrorInfo_OS_SystemCallFailed = 0x10500001, + /// \brief A system call returned an invalid value. + IkarusErrorInfo_OS_InvalidReturnValue = 0x10500002, + /// \brief An OOM error occurred. + IkarusErrorInfo_OS_InsufficientMemory = 0x10500003, + /// \brief A datapoint within ikarus is invalid for the current state of the system. /// Example: The name of an object is found to be invalid UTF8. - IkarusErrorInfo_Type_LibIkarus_InvalidState = 0x0002000300000001, - /// \brief LibIkarus is unable to perform a certain operation that should succeed. - /// Example: Migrating a project fails - IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation = 0x0002000300000002, - /// \brief LibIkarus is unable to perform a certain operation within a given timeframe. + IkarusErrorInfo_LibIkarus_InvalidState = 0x20030001, + /// \brief libikarus is unable to perform a certain operation within a given timeframe. /// Example: A query takes longer than the timeout. - IkarusErrorInfo_Type_LibIkarus_Timeout = 0x0002000300000003, - /// \brief The type of error is unknown. - IkarusErrorInfo_Type_Unknown = 0xFFFFFFFFFFFFFFFF, + IkarusErrorInfo_LibIkarus_Timeout = 0x20030003, +}; + +/// \brief The data limits for an error. +enum IkarusErrorDataLimit { + /// \brief The maximum number of error infos that can be stored in an error. + IkarusErrorDataLimit_MaxErrorInfos = 8, + /// \brief The maximum length of an error message. + IkarusErrorDataLimit_MaxMessageLength = 128, +}; + +/// \brief The data stored for an error +struct IkarusErrorData { + /// \brief The scope of the error. + /// \details This array may at most hold #IkarusErrorDataLimit_MaxErrorInfos elements. + /// The first occurrence of #IkarusErrorInfo_None signifies the end of the array. If this happens at idx x== 0, no error occurred. + IkarusErrorInfo infos[IkarusErrorDataLimit_MaxErrorInfos]; + + char message[IkarusErrorDataLimit_MaxMessageLength]; }; /// \brief Gets the name of an error info. @@ -66,6 +120,20 @@ enum IkarusErrorInfo { /// \remark The returned pointer is valid for the lifetime of the program and must not be freed. IKA_API char const * get_error_info_name(IkarusErrorInfo info); +/// \brief Checks if an error data is a success. +/// \param data The error data to check. +/// \return True if the error data is a success, false otherwise. +IKA_API bool ikarus_error_data_is_success(IkarusErrorData const * data); +/// \brief Checks if an error data is an error. +/// \param data The error data to check. +/// \return True if the error data is an error, false otherwise. +IKA_API bool ikarus_error_data_is_error(IkarusErrorData const * data); +/// \brief Formats the error data in a reasonable but unspecified way. +/// \param data The error data to format. +/// \return The formatted error data. +/// \remark Ownership of the returned pointer is passed to the user and must be freed at their leisure using ikarus_free. +IKA_API char const * ikarus_error_data_pretty_format(IkarusErrorData const * data); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/id.h b/include/ikarus/id.h index fc61b05..ae545a9 100644 --- a/include/ikarus/id.h +++ b/include/ikarus/id.h @@ -28,8 +28,12 @@ IKARUS_BEGIN_HEADER /// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. /// - next 7 bits: #IkarusObjectType /// - last 56 bits: incremented counter generated by the database -using IkarusId = int64_t; +typedef int64_t IkarusId; +/// \brief Creates an id from the given data and type. +/// \param data The data to use for the id. +/// \param type The type to use for the id. +/// \return The created id. IKA_API IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type); /// \brief Fetches the object type of the given id. diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 2163e6f..3ce07ce 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -3,6 +3,7 @@ /// \file blueprint.h /// \author Folling +#include #include #include @@ -24,16 +25,18 @@ struct IkarusBlueprint; /// \param name The name of the blueprint. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param error_out \see errors.h /// \return The created blueprint or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name); +IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Deletes & frees a blueprint. /// \param blueprint The blueprint to delete. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark The blueprint must not be accessed after deletion. -IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); +IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. @@ -42,16 +45,22 @@ IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint); /// \param properties_out The buffer to write the properties to. /// \pre \li Must not be null. /// \param properties_out_size The size of the buffer. +/// \param error_out \see errors.h /// \see ikarus_blueprint_get_property_count -IKA_API void -ikarus_blueprint_get_properties(IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size); +IKA_API void ikarus_blueprint_get_properties( + IkarusBlueprint const * blueprint, + struct IkarusProperty ** properties_out, + size_t properties_out_size, + IkarusErrorData * error_out +); /// \brief Gets the number of properties of a blueprint. /// \param blueprint The blueprint to get the number of properties of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint); +IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); /// \brief Gets the entities linked to a blueprint. /// \param blueprint The blueprint to get the linked entities of. @@ -60,27 +69,34 @@ IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * bluep /// \param entities_out The buffer to write the entities to. /// \pre \li Must not be null. /// \param entities_out_size The size of the buffer. +/// \param error_out \see errors.h /// \see ikarus_blueprint_get_linked_entity_count -IKA_API void -ikarus_blueprint_get_linked_entities(IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size); +IKA_API void ikarus_blueprint_get_linked_entities( + IkarusBlueprint const * blueprint, + struct IkarusEntity ** entities_out, + size_t entities_out_size, + IkarusErrorData * error_out +); /// \brief Gets the number of entities linked to a blueprint. /// \param blueprint The blueprint to get the number of linked entities of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The number of linked entities or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint); +IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); /// \brief Casts a blueprint to an object. /// \param blueprint The blueprint to cast. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The blueprint represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint); +IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \see ikarus_blueprint_to_object -IKA_API struct IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint); +IKA_API struct IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 3e38e25..cfc09f3 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -37,24 +38,21 @@ struct IkarusEntity; /// \param project The project the entity is part of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param parent The parent folder of the entity. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param position The position of the entity in the parent folder. \see #FolderPosition -/// \pre \li Must be within bounds for the parent folder. /// \param name The name of the entity. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param error_out \see errors.h /// \return The created entity or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name); +IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Deletes an entity. /// \param entity The entity to delete. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark The entity must not be accessed after deletion. -IKA_API void ikarus_entity_delete(IkarusEntity * entity); +IKA_API void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); /// \brief Checks if an entity is linked to a blueprint. /// \param entity The entity to check. @@ -63,8 +61,10 @@ IKA_API void ikarus_entity_delete(IkarusEntity * entity); /// \param blueprint The blueprint to check. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return True if the entity is linked to the blueprint, false otherwise. -IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint); +IKA_API bool +ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint, IkarusErrorData * error_out); /// \brief Links an entity to a blueprint. /// \param entity The entity to link. @@ -73,8 +73,9 @@ IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, s /// \param blueprint The blueprint to link the entity to. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark No-op if the entity is already linked to the blueprint. -IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint); +IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \brief Unlinks an entity from a blueprint. All values of the properties of the blueprint the entity is linked with /// will be deleted. @@ -84,8 +85,9 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru /// \param blueprint The blueprint to unlink the entity from. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark No-op if the entity is not linked to the blueprint. -IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint); +IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \brief Checks if an entity has a specific property. /// \param entity The entity to check. @@ -94,15 +96,17 @@ IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct I /// \param property The property to check. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return True if the entity has the property, false otherwise. -IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property); +IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Gets the number of properties of an entity. /// \param entity The entity to get the number of properties of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity); +IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity, IkarusErrorData * error_out); /// \brief Gets the properties of an entity. /// \param entity The entity to get the properties of. @@ -110,8 +114,14 @@ IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity); /// \pre \li Must exist. /// \param properties_out The buffer to write the properties to. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \param properties_out_size The size of the buffer. -IKA_API void ikarus_entity_get_properties(IkarusEntity const * entity, struct IkarusProperty ** properties_out, size_t properties_out_size); +IKA_API void ikarus_entity_get_properties( + IkarusEntity const * entity, + struct IkarusProperty ** properties_out, + size_t properties_out_size, + IkarusErrorData * error_out +); /// \brief Gets the value of a property of an entity. /// \details If the entity has never set the value of the property, the default value is returned (which may be @@ -122,10 +132,12 @@ IKA_API void ikarus_entity_get_properties(IkarusEntity const * entity, struct Ik /// \param property The property to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The value of the property or null if the entity does not have the property or an error occurs. /// \remark Must be freed using /// #ikarus_free. -IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property); +IKA_API struct IkarusEntityValue * +ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Sets the value of a property of an entity. /// \param entity The entity to set the value of. @@ -138,19 +150,26 @@ IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * /// \pre \li Must not be null. /// \pre \li Must be of the same type as the property. /// \pre \li Must be valid for the property's settings. +/// \param error_out \see errors.h /// \remark If the entity does not have the property, this function fails. -IKA_API void ikarus_entity_set_value(IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value); +IKA_API void ikarus_entity_set_value( + IkarusEntity * entity, + struct IkarusProperty const * property, + struct IkarusValue const * value, + IkarusErrorData * error_out +); /// \brief Casts an entity to an object. /// \param entity The entity to cast. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The entity represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity); +IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity, IkarusErrorData * error_out); /// \see ikarus_entity_to_object -IKA_API struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity); +IKA_API struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index ba754c5..2aa3d0c 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -3,6 +3,7 @@ /// \file object.h /// \author Folling +#include #include /// \defgroup object Objects @@ -20,10 +21,14 @@ struct IkarusObject; /// \brief Compares two objects for equality. /// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the -/// same object. \param lhs The left hand side object. \pre \li Must not be null. \param rhs The right hand side object. +/// same object. +/// \param lhs The left hand side object. /// \pre \li Must not be null. +/// \param rhs The right hand side object. +/// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the objects are equal, false otherwise. -IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs); +IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out); /// \brief Visits an object. Calling the appropriate function for the object's type. /// \param object The object to visit. @@ -34,6 +39,7 @@ IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const /// \param property_folder_visitor The function to call if the object is a property folder. Skipped if null. /// \param entity_folder_visitor The function to call if the object is an entity folder. Skipped if null. /// \param data The data passed to the visitor functions. +/// \param error_out \see errors.h IKA_API void ikarus_object_visit( IkarusObject * object, void (*blueprint_visitor)(struct IkarusBlueprint *, void *), @@ -42,7 +48,8 @@ IKA_API void ikarus_object_visit( void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder *, void *), void (*property_folder_visitor)(struct IkarusPropertyFolder *, void *), void (*entity_folder_visitor)(struct IkarusEntityFolder *, void *), - void * data + void * data, + IkarusErrorData * error_out ); /// \see ikarus_object_visit @@ -54,7 +61,8 @@ IKA_API void ikarus_object_visit_const( void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder const *, void *), void (*property_folder_visitor)(struct IkarusPropertyFolder const *, void *), void (*entity_folder_visitor)(struct IkarusEntityFolder const *, void *), - void * data + void * data, + IkarusErrorData * error_out ); IKARUS_END_HEADER diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 4298a9b..7dd3507 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -3,6 +3,7 @@ /// \file object_type.h /// \author Folling +#include #include /// \addtogroup objects Objects @@ -24,9 +25,10 @@ enum IkarusObjectType { /// \brief Converts an IkarusObjectType to a string. /// \param type The type to convert. +/// \param error_out \see errors.h /// \return The string representation of the type. /// \remark The returned string must not be freed. -char const * ikarus_object_type_to_string(IkarusObjectType type); +char const * ikarus_object_type_to_string(IkarusObjectType type, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 3108201..5d24f87 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -3,24 +3,44 @@ /// \file number_property.h /// \author Folling +#include #include /// \addtogroup properties Properties -/// \brief Number properties store a numeric value. (e.g. "Weight" or "Age") +/// \brief Number properties store a value that can be either true or false. (e.g. "Is the character dead?") /// @{ IKARUS_BEGIN_HEADER struct IkarusNumberProperty; -IKA_API IkarusNumberProperty * -ikarus_number_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); +/// \brief Creates a number property. +/// \param project The project to create the property in. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param property_source The property source to create the property for. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The created property or null if an error occurs. +IKA_API IkarusNumberProperty * ikarus_number_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +); /// \brief Sets the default value for a number property. /// \param property The number property. /// \pre \li Must not be null. /// \pre \li Must exist. -IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(struct IkarusNumberProperty * property); +/// \param error_out \see errors.h +/// \return The default value or null if an error occurs. +IKA_API struct IkarusNumberValue * +ikarus_number_property_get_default_value(struct IkarusNumberProperty * property, IkarusErrorData * error_out); /// \brief Sets the default value for a number property. /// \param property The number property. @@ -29,9 +49,14 @@ IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(stru /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. +/// \param error_out \see errors.h /// \remark Please see \ref property.h "the property documentation" for more information on the interplay between /// default values and other settings. -IKA_API void ikarus_number_property_set_default_value(struct IkarusNumberProperty * property, struct IkarusNumberValue * default_value); +IKA_API void ikarus_number_property_set_default_value( + struct IkarusNumberProperty * property, + struct IkarusNumberValue * default_value, + IkarusErrorData * error_out +); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index ef28ca2..34f2d9a 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -3,6 +3,7 @@ /// \file property.h /// \author Folling +#include #include #include #include @@ -59,32 +60,36 @@ struct IkarusProperty; /// \param property The property to delete. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark The property must not be accessed after deletion. -IKA_API void ikarus_property_delete(IkarusProperty * property); +IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * error_out); /// \brief Gets the type info of a property. /// \param property The property to get the type info of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The type info of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property); +IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Gets the source of a property. /// \param property The property to get the source of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The source of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property); +IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Gets the default value of a property. /// \param property The property to get the type info of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The default value of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusProperty const * property); +IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Visits a property. Calling the appropriate function for the property's type. /// \param property The property to visit. @@ -94,12 +99,14 @@ IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusPrope /// \param number_property_visitor The function to call if the property is a number property. Skipped if null. /// \param text_property_visitor The function to call if the property is a text property. Skipped if null. /// \param data The data to pass to the functions. +/// \param error_out \see errors.h IKA_API void ikarus_property_visit( IkarusProperty * property, void (*toggle_property_visitor)(struct IkarusToggleProperty *, void *), void (*number_property_visitor)(struct IkarusNumberProperty *, void *), void (*text_property_visitor)(struct IkarusTextProperty *, void *), - void * data + void * data, + IkarusErrorData * error_out ); /// \see ikarus_property_visit @@ -108,19 +115,21 @@ IKA_API void ikarus_property_visit_const( void (*toggle_property_visitor)(struct IkarusToggleProperty const *, void *), void (*number_property_visitor)(struct IkarusNumberProperty const *, void *), void (*text_property_visitor)(struct IkarusTextProperty const *, void *), - void * data + void * data, + IkarusErrorData * error_out ); /// \brief Casts a property to an object. /// \param property The property to cast. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The property represented as an object or null if an error occurs. /// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty * property); +IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty * property, IkarusErrorData * error_out); /// \see ikarus_property_to_object -IKA_API struct IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property); +IKA_API struct IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/property_source.h b/include/ikarus/objects/properties/property_source.h index 275baa4..f1da29d 100644 --- a/include/ikarus/objects/properties/property_source.h +++ b/include/ikarus/objects/properties/property_source.h @@ -3,6 +3,7 @@ /// \file property_source.h /// \author Folling +#include #include /// \addtogroup properties Properties @@ -16,17 +17,20 @@ struct IkarusPropertySource; /// \param blueprint The blueprint to create the property source for. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint); +IKA_API struct IkarusPropertySource * +ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \brief Creates an entity property source. /// \param entity The entity to create the property source for. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity); +IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity, IkarusErrorData * error_out); /// \brief Visits a property source, calling the appropriate callback. /// \param property_source The property source to visit. @@ -35,11 +39,13 @@ IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struc /// \param blueprint_visitor The callback to call if the source is a blueprint, skipped if null. /// \param entity_visitor The callback to call if the source is an entity, skipped if null. /// \param user_data User data to pass to the callbacks. +/// \param error_out \see errors.h IKA_API void ikarus_property_source_visit( struct IkarusPropertySource * property_source, void (*blueprint_visitor)(struct IkarusBlueprint *, void *), void (*entity_visitor)(struct IkarusEntity *, void *), - void * user_data + void * user_data, + IkarusErrorData * error_out ); /// \see ikarus_property_source_visit @@ -47,7 +53,8 @@ IKA_API void ikarus_property_source_visit_const( struct IkarusPropertySource const * property_source, void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), void (*entity_visitor)(struct IkarusEntity const *, void *), - void * user_data + void * user_data, + IkarusErrorData * error_out ); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index 9c91c80..0d240b2 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -3,24 +3,43 @@ /// \file text_property.h /// \author Folling +#include #include /// \addtogroup properties Properties -/// \brief Text properties store an arbitrary piece of text. (e.g. "Firstname" or "Description") +/// \brief Text properties store a value that can be either true or false. (e.g. "Is the character dead?") /// @{ IKARUS_BEGIN_HEADER struct IkarusTextProperty; -IKA_API IkarusTextProperty * -ikarus_text_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); +/// \brief Creates a text property. +/// \param project The project to create the property in. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param property_source The property source to create the property for. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The created property or null if an error occurs. +IKA_API IkarusTextProperty * ikarus_text_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +); /// \brief Sets the default value for a text property. /// \param property The text property. /// \pre \li Must not be null. /// \pre \li Must exist. -IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property); +/// \param error_out \see errors.h +/// \return The default value or null if an error occurs. +IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property, IkarusErrorData * error_out); /// \brief Sets the default value for a text property. /// \param property The text property. @@ -29,9 +48,14 @@ IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct I /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. +/// \param error_out \see errors.h /// \remark Please see \ref property.h "the property documentation" for more information on the interplay between /// default values and other settings. -IKA_API void ikarus_text_property_set_default_value(struct IkarusTextProperty * property, struct IkarusTextValue * default_value); +IKA_API void ikarus_text_property_set_default_value( + struct IkarusTextProperty * property, + struct IkarusTextValue * default_value, + IkarusErrorData * error_out +); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index cfa51e3..e084123 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -3,6 +3,7 @@ /// \file toggle_property.h /// \author Folling +#include #include /// \addtogroup properties Properties @@ -13,14 +14,33 @@ IKARUS_BEGIN_HEADER struct IkarusToggleProperty; -IKA_API IkarusToggleProperty * -ikarus_toggle_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source); +/// \brief Creates a toggle property. +/// \param project The project to create the property in. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the property. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param property_source The property source to create the property for. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The created property or null if an error occurs. +IKA_API IkarusToggleProperty * ikarus_toggle_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +); /// \brief Sets the default value for a toggle property. /// \param property The toggle property. /// \pre \li Must not be null. /// \pre \li Must exist. -IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property); +/// \param error_out \see errors.h +/// \return The default value or null if an error occurs. +IKA_API struct IkarusToggleValue * +ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, IkarusErrorData * error_out); /// \brief Sets the default value for a toggle property. /// \param property The toggle property. @@ -29,9 +49,14 @@ IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(stru /// \param default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. +/// \param error_out \see errors.h /// \remark Please see \ref property.h "the property documentation" for more information on the interplay between /// default values and other settings. -IKA_API void ikarus_toggle_property_set_default_value(struct IkarusToggleProperty * property, struct IkarusToggleValue * default_value); +IKA_API void ikarus_toggle_property_set_default_value( + struct IkarusToggleProperty * property, + struct IkarusToggleValue * default_value, + IkarusErrorData * error_out +); IKARUS_END_HEADER diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 7df0e45..93c4e11 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -25,28 +25,31 @@ struct IkarusProject; /// \param name The name of the project. Must neither be null nor empty. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param error_out \see errors.h /// \return The created project or null if an error occurs. /// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_create(char const * path, char const * name); +IKA_API IkarusProject * ikarus_project_create(char const * path, char const * name, IkarusErrorData * error_out); /// \brief Creates a project in memory. /// \param name The name of the project. Must neither be null nor empty. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param error_out \see errors.h /// \return The created project or null if an error occurs. /// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name); +IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out); /// \brief Opens an existing project. /// \param path The path to the project. /// \pre \li Must not be null. /// \pre \li Must point to an existing project on the system. +/// \param error_out \see errors.h /// \return The opened project or null if an error occurs. /// \remark Must be freed using ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_open(char const * path); +IKA_API IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_out); /// \brief Copies a project to a new location. /// \details The new project is not opened. @@ -59,25 +62,29 @@ IKA_API IkarusProject * ikarus_project_open(char const * path); /// \param target_name The name of the new project. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param error_out \see errors.h /// \remark If successful the project connection remains intact. The previous location will still exist. -IKA_API void ikarus_project_copy(IkarusProject const * project, char const * target_path, char const * target_name); +IKA_API void +ikarus_project_copy(IkarusProject const * project, char const * target_path, char const * target_name, IkarusErrorData * error_out); /// \brief Deletes a project and all its associated data from the filesystem. /// \param project The project to delete. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \remark also frees the project. /// \remark In-Memory projects will just be freed. /// \remark If deletion fails, the project pointer remains intact. -IKA_API void ikarus_project_delete(IkarusProject * project); +IKA_API void ikarus_project_delete(IkarusProject * project, IkarusErrorData * error_out); /// \brief Gets the name of a project. /// \param project The project to get the name of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The name of the project. /// \remark Must be freed using #ikarus_free. -IKA_API char const * ikarus_project_get_name(IkarusProject const * project); +IKA_API char const * ikarus_project_get_name(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Sets the name of a project. /// \param project The project to set the name of. @@ -86,15 +93,17 @@ IKA_API char const * ikarus_project_get_name(IkarusProject const * project); /// \param new_name The new name of the project. /// \pre \li Must not be null. /// \pre \li Must not be empty. -IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_name); +/// \param error_out \see errors.h +IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_name, IkarusErrorData * error_out); /// \brief Gets the path of a project. /// \param project The project to get the path of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The path of the project. /// \remark Must be freed using #ikarus_free. -IKA_API char const * ikarus_project_get_path(IkarusProject const * project); +IKA_API char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Moves a project to a new location. /// \param project The project to move. @@ -103,39 +112,19 @@ IKA_API char const * ikarus_project_get_path(IkarusProject const * project); /// \param target_path The new location of the project. /// \pre \li Must not be null. /// \pre \li Must point to a valid unused path on the system. +/// \param error_out \see errors.h /// \remark If successful the project connection remains intact. The previous location will not exist anymore. /// \remark Due to the nature of filesystems this function may not be atomic. -IKA_API void ikarus_project_move(IkarusProject * project, char const * target_path); - -/// \brief Gets the error code of a project. -/// \param project The project to get the error code of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The error code of the project. -IKA_API int ikarus_project_get_error_code(IkarusProject const * project); -/// \brief Gets the error message of a project. -/// \param project The project to get the error message of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The error message of the project. -/// \remark The returned pointer is valid until the project is freed but may be altered by other operations. -/// \warning Must not be freed. -IKA_API char const * ikarus_project_get_error_message(IkarusProject const * project); +IKA_API void ikarus_project_move(IkarusProject * project, char const * target_path, IkarusErrorData * error_out); /// \brief Gets the blueprint root folder of a project. /// \param project The project to get the blueprint root folder of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The blueprint root folder of the project or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder(IkarusProject const * project); - -/// \brief Gets the number of blueprints of a project. -/// \param project The project to get the number of blueprints of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project); +IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Gets the blueprints of a project. /// \param project The project to get the blueprints of. @@ -144,23 +133,30 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project) /// \param blueprints_out The buffer to write the blueprints to. /// \pre \li Must not be null. /// \param blueprints_out_size The size of the buffer. -IKA_API void -ikarus_project_get_blueprints(IkarusProject const * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size); +/// \param error_out \see errors.h +IKA_API void ikarus_project_get_blueprints( + IkarusProject const * project, + struct IkarusBlueprint ** blueprints_out, + size_t blueprints_out_size, + IkarusErrorData * error_out +); + +/// \brief Gets the number of blueprints of a project. +/// \param project The project to get the number of blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The number of blueprints or undefined if an error occurs. +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Gets the entity root folder of a project. /// \param project The project to get the entity root folder of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param error_out \see errors.h /// \return The entity root folder of the project or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(IkarusProject const * project); - -/// \brief Gets the number of entities of a project. -/// \param project The project to get the number of entities of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \return The number of entities or undefined if an error occurs. -IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project); +IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Gets the entities of a project. /// \param project The project to get the entities of. @@ -169,7 +165,21 @@ IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project); /// \param entities_out The buffer to write the entities to. /// \pre \li Must not be null. /// \param entities_out_size The size of the buffer. -IKA_API void ikarus_project_get_entities(IkarusProject const * project, struct IkarusEntity ** entities_out, size_t entities_out_size); +/// \param error_out \see errors.h +IKA_API void ikarus_project_get_entities( + IkarusProject const * project, + struct IkarusEntity ** entities_out, + size_t entities_out_size, + IkarusErrorData * error_out +); + +/// \brief Gets the number of entities of a project. +/// \param project The project to get the number of entities of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The number of entities or undefined if an error occurs. +IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index cedd61d..1425fed 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -3,8 +3,8 @@ /// \file number_value.h /// \author Folling +#include #include -#include /// \addtogroup values Values /// @{ @@ -14,24 +14,27 @@ IKARUS_BEGIN_HEADER /// \brief A numeric value. For example "Age" or "Height". struct IkarusNumberValue; -/// \brief Creates a number value. +/// \brief Creates an empty number value. +/// \param error_out \see errors.h /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create(); +IKA_API IkarusNumberValue * ikarus_number_value_create(IkarusErrorData * error_out); /// \brief Fetches the underlying data of a number value at a specific index. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. -/// \return The underlying data or null if an error occurs or the value is undefined. -IKA_API double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx); +/// \param error_out \see errors.h +/// \return The underlying data or NaN if an error occurs or the value is undefined. +IKA_API double ikarus_number_value_get(IkarusNumberValue const * value, size_t idx, IkarusErrorData * error_out); /// \brief Fetches the size of the underlying data of a number value. /// \param value The number value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The size of the underlying data or 0 if an error occurs or the value is undefined. -IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); +IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Sets the data of a number value at a specific index. /// \param value The number value. @@ -39,14 +42,16 @@ IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value); /// \param idx The index of the data to set. /// \pre \li Must be less than the size of the value. /// \param new_data The new data. -IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double const * new_data); +/// \param error_out \see errors.h +IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double new_data, IkarusErrorData * error_out); /// \brief Removes a data from a number value. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to remove. /// \pre \li Must be less than the size of the value. -IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); +/// \param error_out \see errors.h +IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx, IkarusErrorData * error_out); /// \brief Inserts a data into a number value. /// \param value The number value. @@ -54,60 +59,68 @@ IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx); /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. /// \param new_data The new data. -IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, double const * new_data); +/// \param error_out \see errors.h +IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, double new_data, IkarusErrorData * error_out); /// \brief Clears a number value. /// \param value The number value. +/// \param error_out \see errors.h /// \remark Noop if the value is undefined. -IKA_API void ikarus_number_value_clear(IkarusNumberValue * value); +IKA_API void ikarus_number_value_clear(IkarusNumberValue * value, IkarusErrorData * error_out); /// \brief Checks if a number value is undefined. /// \param value The number value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_number_value_is_undefined(IkarusNumberValue const * value); +IKA_API bool ikarus_number_value_is_undefined(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Changes a number value's undefined state. /// \param value The number value. /// \pre \li Must not be null. /// \param undefined The new undefined state. +/// \param error_out \see errors.h /// \remark Noop if the value is already undefined. /// \remark If the value is set to undefined, all data will be cleared. /// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined); +IKA_API void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined, IkarusErrorData * error_out); /// \brief Converts a number value to a string. /// \param value The number value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. /// \remark Undefined if the value is undefined. -IKA_API char const * ikarus_number_value_to_string(IkarusNumberValue const * value); +IKA_API char const * ikarus_number_value_to_string(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. /// \param lhs The left hand side value. /// \pre \li Must not be null. /// \param rhs The right hand side value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the values' data are equal, false otherwise. -IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs); +IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs, IkarusErrorData * error_out); /// \brief Creates a copy of a number value. /// \param value The value to copy. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The copied value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value); +IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Converts a number value to an entity value. /// \param value The number value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value); +IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value, IkarusErrorData * error_out); /// \see ikarus_toggle_value_to_value -IKA_API struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value); +IKA_API struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index b40a7f6..6511eb5 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -3,6 +3,7 @@ /// \file text_value.h /// \author Folling +#include #include #include @@ -14,101 +15,115 @@ IKARUS_BEGIN_HEADER /// \brief A textual value. For example "Surname" or "Description" struct IkarusTextValue; -/// \brief Creates a text value. +/// \brief Creates an empty text value. +/// \param error_out \see errors.h /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create(); +IKA_API IkarusTextValue * ikarus_text_value_create(IkarusErrorData * error_out); /// \brief Fetches the underlying data of a number value at a specific index. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. +/// \param error_out \see errors.h /// \return The underlying data or null if an error occurs or the value is undefined. -/// \remark This value is owned by LibIkarus and must not be freed. -IKA_API char const * ikarus_text_value_get(IkarusTextValue * value, size_t idx); +IKA_API char const * const * ikarus_text_value_get(IkarusTextValue const * value, size_t idx, IkarusErrorData * error_out); /// \brief Fetches the size of the underlying data of a text value. /// \param value The text value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The size of the underlying data or 0 if an error occurs or the value is undefined. -IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value); +IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Sets the data of a text value at a specific index. /// \param value The text value. /// \pre \li Must not be null. /// \param idx The index of the data to set. /// \pre \li Must be less than the size of the value. -/// \param new_data The new data. LibIkarus assumes ownership of this pointer. -IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data); +/// \param new_data The new data. Ownership remains with the caller. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data, IkarusErrorData * error_out); /// \brief Removes a data from a text value. /// \param value The text value. /// \pre \li Must not be null. /// \param idx The index of the data to remove. /// \pre \li Must be less than the size of the value. -IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx); +/// \param error_out \see errors.h +IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx, IkarusErrorData * error_out); /// \brief Inserts a data into a text value. /// \param value The text value. /// \pre \li Must not be null. /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. -/// \param new_data The new data. -IKA_API void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data); +/// \param new_data The new data. Ownership remains with the caller. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +IKA_API void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data, IkarusErrorData * error_out); /// \brief Clears a text value. /// \param value The text value. +/// \param error_out \see errors.h /// \remark Noop if the value is undefined. -IKA_API void ikarus_text_value_clear(IkarusTextValue * value); +IKA_API void ikarus_text_value_clear(IkarusTextValue * value, IkarusErrorData * error_out); /// \brief Checks if a text value is undefined. /// \param value The text value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_text_value_is_undefined(IkarusTextValue const * value); +IKA_API bool ikarus_text_value_is_undefined(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Changes a text value's undefined state. /// \param value The text value. /// \pre \li Must not be null. /// \param undefined The new undefined state. +/// \param error_out \see errors.h /// \remark Noop if the value is already undefined. /// \remark If the value is set to undefined, all data will be cleared. /// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined); +IKA_API void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined, IkarusErrorData * error_out); /// \brief Converts a text value to a string. /// \param value The text value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. /// \remark Undefined if the value is undefined. -IKA_API char const * ikarus_text_value_to_string(IkarusTextValue const * value); +IKA_API char const * ikarus_text_value_to_string(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. /// \param lhs The left hand side value. /// \pre \li Must not be null. /// \param rhs The right hand side value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the values' data are equal, false otherwise. -IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs); +IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs, IkarusErrorData * error_out); /// \brief Creates a copy of a text value. /// \param value The value to copy. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The copied value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value); +IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Converts a text value to an entity value. /// \param value The text value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value); +IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value, IkarusErrorData * error_out); -/// \see ikarus_toggle_value_to_value -IKA_API struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value); +/// \see ikarus_text_value_to_value +IKA_API struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index 2bb84c7..9e1615b 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -3,6 +3,7 @@ /// \file toggle_value.h /// \author Folling +#include #include #include @@ -14,24 +15,27 @@ IKARUS_BEGIN_HEADER /// \brief A true/false boolean-esque value. For example "Is Dead". struct IkarusToggleValue; -/// \brief Creates a toggle. +/// \brief Creates an empty toggle value. +/// \param error_out \see errors.h /// \return The value or null if an error occurs. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create(); +IKA_API IkarusToggleValue * ikarus_toggle_value_create(IkarusErrorData * error_out); /// \brief Fetches the underlying data of a number value at a specific index. /// \param value The number value. /// \pre \li Must not be null. /// \param idx The index of the data to fetch. /// \pre \li Must be less than the size of the value. +/// \param error_out \see errors.h /// \return The underlying data or null if an error occurs or the value is undefined. -IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue * value, size_t idx); +IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue const * value, size_t idx, IkarusErrorData * error_out); /// \brief Fetches the size of the underlying data of a toggle value. /// \param value The toggle value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The size of the underlying data or 0 if an error occurs or the value is undefined. -IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value); +IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Sets the data of a toggle value at a specific index. /// \param value The toggle value. @@ -39,14 +43,16 @@ IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value); /// \param idx The index of the data to set. /// \pre \li Must be less than the size of the value. /// \param new_data The new data. -IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data); +/// \param error_out \see errors.h +IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data, IkarusErrorData * error_out); /// \brief Removes a data from a toggle value. /// \param value The toggle value. /// \pre \li Must not be null. /// \param idx The index of the data to remove. /// \pre \li Must be less than the size of the value. -IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx); +/// \param error_out \see errors.h +IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx, IkarusErrorData * error_out); /// \brief Inserts a data into a toggle value. /// \param value The toggle value. @@ -54,60 +60,68 @@ IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx); /// \param idx The index of the data to insert. /// \pre \li Must be less than or equal to the size of the value. /// \param new_data The new data. -IKA_API void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data); +/// \param error_out \see errors.h +IKA_API void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data, IkarusErrorData * error_out); /// \brief Clears a toggle value. /// \param value The toggle value. +/// \param error_out \see errors.h /// \remark Noop if the value is undefined. -IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value); +IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value, IkarusErrorData * error_out); /// \brief Checks if a toggle value is undefined. /// \param value The toggle value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value); +IKA_API bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Changes a toggle value's undefined state. /// \param value The toggle value. /// \pre \li Must not be null. /// \param undefined The new undefined state. +/// \param error_out \see errors.h /// \remark Noop if the value is already undefined. /// \remark If the value is set to undefined, all data will be cleared. /// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined); +IKA_API void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined, IkarusErrorData * error_out); /// \brief Converts a toggle value to a string. /// \param value The toggle value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. /// \remark Undefined if the value is undefined. -IKA_API char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value); +IKA_API char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. /// \param lhs The left hand side value. /// \pre \li Must not be null. /// \param rhs The right hand side value. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return True if the values' data are equal, false otherwise. -IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs); +IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs, IkarusErrorData * error_out); /// \brief Creates a copy of a toggle value. /// \param value The value to copy. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The copied value. /// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value); +IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Converts a toggle value to an entity value. /// \param value The toggle value to convert. /// \pre \li Must not be null. +/// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value); +IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value, IkarusErrorData * error_out); /// \see ikarus_toggle_value_to_value -IKA_API struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value); +IKA_API struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index 880c2eb..3fcaf63 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -3,6 +3,7 @@ /// \file value.h /// \author Folling +#include #include /// \defgroup values Values @@ -11,7 +12,7 @@ /// These value classes represent plain objects. They are not associated with any entity. /// Each value may be undefined. \see IkarusProperty /// Values are stored as lists. If a property is "singular" then its value is a list of size 1. -/// Values are typed, with types existing for each of the corresponding property types. +/// Values are typed, with types existing for each of the corresponding property types. The data of values starts with the index 0. /// When setting values for a property the type must match the property type and the value must be valid under the /// property's settings. \see PropertyType /// @{ @@ -28,12 +29,14 @@ struct IkarusValue; /// \param number_visitor The function to call if the value is a number value. Skipped if null. /// \param text_visitor The function to call if the value is a text value. Skipped if null. /// \param data The data passed to the visitor functions. +/// \param error_out \see errors.h IKA_API void ikarus_value_visit( IkarusValue * value, void (*toggle_visitor)(struct IkarusToggleValue *, void *), void (*number_visitor)(struct IkarusNumberValue *, void *), void (*text_visitor)(struct IkarusTextValue *, void *), - void * data + void * data, + IkarusErrorData * error_out ); /// \see ikarus_value_visit @@ -42,7 +45,8 @@ IKA_API void ikarus_value_visit_const( void (*toggle_visitor)(struct IkarusToggleValue const *, void *), void (*number_visitor)(struct IkarusNumberValue const *, void *), void (*text_visitor)(struct IkarusTextValue const *, void *), - void * data + void * data, + IkarusErrorData * error_out ); IKARUS_END_HEADER diff --git a/src/errors.cpp b/src/errors.cpp index 54681f2..32b2c99 100644 --- a/src/errors.cpp +++ b/src/errors.cpp @@ -1,22 +1,71 @@ #include "ikarus/errors.h" +#include "cppbase/functional.hpp" + +#include + +#include + +#include + char const * get_error_info_name(IkarusErrorInfo info) { switch (info) { - case IkarusErrorInfo_Source_None: return "IkarusErrorInfo_Source_None"; - case IkarusErrorInfo_Source_Client: return "IkarusErrorInfo_Source_Client"; - case IkarusErrorInfo_Source_SubSystem: return "IkarusErrorInfo_Source_SubSystem"; - case IkarusErrorInfo_Source_LibIkarus: return "IkarusErrorInfo_Source_LibIkarus"; - case IkarusErrorInfo_Source_Unknown: return "IkarusErrorInfo_Source_Unknown"; - case IkarusErrorInfo_Type_None: return "IkarusErrorInfo_Type_None"; - case IkarusErrorInfo_Type_Client_Misuse: return "IkarusErrorInfo_Type_Client_Misuse"; - case IkarusErrorInfo_Type_Client_Input: return "IkarusErrorInfo_Type_Client_Input"; - case IkarusErrorInfo_Type_SubSystem_Dependency: return "IkarusErrorInfo_Type_SubSystem_Dependency"; - case IkarusErrorInfo_Type_SubSystem_Database: return "IkarusErrorInfo_Type_SubSystem_Database"; - case IkarusErrorInfo_Type_SubSystem_Filesystem: return "IkarusErrorInfo_Type_SubSystem_Filesystem"; - case IkarusErrorInfo_Type_LibIkarus_InvalidState: return "IkarusErrorInfo_Type_LibIkarus_InvalidState"; - case IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation: return "IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation"; - case IkarusErrorInfo_Type_LibIkarus_Timeout: return "IkarusErrorInfo_Type_LibIkarus_Timeout"; - case IkarusErrorInfo_Type_Unknown: return "IkarusErrorInfo_Type_Unknown"; - default: return "Unknown"; + case IkarusErrorInfo_None: return "None"; + case IkarusErrorInfo_Client: return "Client"; + case IkarusErrorInfo_Dependency: return "Dependency"; + case IkarusErrorInfo_Filesystem: return "Filesystem"; + case IkarusErrorInfo_Database: return "Database"; + case IkarusErrorInfo_OS: return "OS"; + case IkarusErrorInfo_LibIkarus: return "libikarus"; + + case IkarusErrorInfo_Client_Misuse: return "Misuse"; + case IkarusErrorInfo_Client_InvalidInput: return "InvalidInput"; + case IkarusErrorInfo_Client_InvalidFormat: return "InvalidFormat"; + case IkarusErrorInfo_Client_ConstraintViolated: return "ConstraintViolated"; + + case IkarusErrorInfo_Filesystem_NotFound: return "NotFound"; + case IkarusErrorInfo_Filesystem_AlreadyExists: return "AlreadyExists"; + case IkarusErrorInfo_Filesystem_MissingPermissions: return "MissingPermissions"; + case IkarusErrorInfo_Filesystem_InsufficientSpace: return "InsufficientSpace"; + case IkarusErrorInfo_Filesystem_InvalidPath: return "InvalidPath"; + + case IkarusErrorInfo_Database_ConnectionFailed: return "ConnectionFailed"; + case IkarusErrorInfo_Database_QueryFailed: return "QueryFailed"; + case IkarusErrorInfo_Database_MigrationFailed: return "MigrationFailed"; + case IkarusErrorInfo_Database_InvalidState: return "InvalidState"; + + case IkarusErrorInfo_OS_SystemCallFailed: return "SystemCallFailed"; + case IkarusErrorInfo_OS_InvalidReturnValue: return "InvalidReturnValue"; + case IkarusErrorInfo_OS_InsufficientMemory: return "InsufficientMemory"; + + case IkarusErrorInfo_LibIkarus_InvalidState: return "InvalidState"; + case IkarusErrorInfo_LibIkarus_Timeout: return "Timeout"; + + default: return "Invalid"; } } + +bool ikarus_error_data_is_success(IkarusErrorData const * data) { + return data->infos[0] == IkarusErrorInfo_None; +} + +bool ikarus_error_data_is_error(IkarusErrorData const * data) { + return data->infos[0] != IkarusErrorInfo_None; +} + +char const * ikarus_error_data_pretty_format(IkarusErrorData const * data) { + if (ikarus_error_data_is_success(data)) { + return "Success"; + } + + auto const formatted = fmt::format( + "{} - {}", + fmt::join( + data->infos | std::views::take_while(cppbase::pred_ne(IkarusErrorInfo_None)) | std::views::transform(get_error_info_name), + "->" + ), + data->message + ); + + return strndup(formatted.data(), formatted.size()); +} diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index c15c7bc..117cbfc 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -1,7 +1,6 @@ #include "ikarus/objects/blueprint.h" #include "objects/blueprint.hpp" -#include "util.hpp" #include #include @@ -9,25 +8,44 @@ #include #include -#include -#include #include IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusObject{project, id} {} IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { - return ikarus::util::insert_object( - project, - IkarusObjectType_Blueprint, - name, - [](auto * db, IkarusId id) { return db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id); }, - [project](IkarusId id) { return project->get_blueprint(id); } - ).unwrap_value_or(nullptr); + if (cppbase::is_empty_or_blank(name)) { + project->set_error("blueprint name must not be empty", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); + + return nullptr; + } + + project->db + ->transact([name](auto * db) { + TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Blueprint, name)); + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); + TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id)); + return cppbase::ok(); + }) + .on_error([project](auto const & err) { + project->set_error( + fmt::format("failed to create blueprint: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }); } void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { - ikarus::util::delete_object(blueprint); + blueprint->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id).on_error([blueprint](auto const & err) { + blueprint->project->set_error( + fmt::format("failed to delete blueprint from objects table: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }); } void ikarus_blueprint_get_properties( @@ -35,41 +53,34 @@ void ikarus_blueprint_get_properties( struct IkarusProperty ** properties_out, size_t properties_out_size ) { - ikarus::util::fetch_multiple_buffered( - blueprint, - ikarus::util::MultipleBufferQueryData{ - .table_name = "properties", - .select_field_name = "id", - .where_field_name = "blueprint", - .relation_desc = "properties" - }, - properties_out, - properties_out_size, - [&](IkarusProject * project, IkarusFunctionContext * ctx, IkarusId id) -> cppbase::Result { - VTRY(auto const type, IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const & err) { - ctx->set_error( - fmt::format("failed to fetch property {}'s type: {}", id, err), + IkarusId ids[properties_out_size]; + + TRYRV( + , + blueprint->project->db + ->query_many_buffered("SELECT `id` FROM `properties` WHERE `source` = ?", ids, properties_out_size, blueprint->id) + .on_error([&](auto const & err) { + blueprint->project->set_error( + fmt::format("failed to fetch blueprint properties from database: {}", err), true, IkarusErrorInfo_Source_SubSystem, IkarusErrorInfo_Type_SubSystem_Database ); - })); - - return cppbase::ok(project->get_property(id, type)); - } + }) ); + + // not atomic, could be switched to two loops if necessary + for (size_t i = 0; i < properties_out_size; ++i) { + IkarusId id = ids[i]; + + VTRYRV(auto const type, , IkarusProperty::get_property_type(blueprint->project, id)); + + properties_out[i] = blueprint->project->get_property(id, type); + } } size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) { - return ikarus::util::fetch_count( - blueprint, - ikarus::util::CountQueryData{ - .table_name = "blueprint_properties", - .select_field_name = "property", - .where_field_name = "blueprint", - .relation_desc = "properties" - } - ) + return blueprint->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", blueprint->id) .unwrap_value_or(0); } @@ -78,31 +89,34 @@ void ikarus_blueprint_get_linked_entities( struct IkarusEntity ** entities_out, size_t entities_out_size ) { - ikarus::util::fetch_multiple_buffered( - blueprint, - ikarus::util::MultipleBufferQueryData{ - .table_name = "entity_blueprint_links", - .select_field_name = "entity", - .where_field_name = "blueprint", - .relation_desc = "linked entities" - }, - entities_out, - entities_out_size, - [&](IkarusProject * project, [[maybe_unused]] IkarusFunctionContext * ctx, IkarusId id - ) -> cppbase::Result { return cppbase::ok(project->get_entity(id)); } + IkarusId ids[entities_out_size]; + + TRYRV( + , + blueprint->project->db + ->query_many_buffered( + "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", + ids, + entities_out_size, + blueprint->id + ) + .on_error([&](auto const & err) { + blueprint->project->set_error( + fmt::format("failed to fetch linked entities from database: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) ); + + for (size_t i = 0; i < entities_out_size; ++i) { + entities_out[i] = blueprint->project->get_entity(ids[i]); + } } size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) { - return ikarus::util::fetch_count( - blueprint, - ikarus::util::CountQueryData{ - .table_name = "entity_blueprint_links", - .select_field_name = "entity", - .where_field_name = "blueprint", - .relation_desc = "linked entities" - } - ) + return blueprint->project->db->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) .unwrap_value_or(0); } diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp index 80d047e..af2395f 100644 --- a/src/objects/entity.cpp +++ b/src/objects/entity.cpp @@ -4,7 +4,6 @@ #include #include -#include #include IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name) { diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index d28275d..498e321 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -23,12 +22,10 @@ IkarusProperty::Data const & IkarusProperty::get_data() const { } cppbase::Result IkarusProperty::get_property_type(IkarusProject * project, IkarusId id) { - auto * ctx = project->get_function_context(); - VTRY( auto const type, - project->get_db()->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id).on_error([ctx](auto const & err) { - ctx->set_error( + project->db->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id).on_error([project](auto const & err) { + project->set_error( fmt::format("failed to fetch unboxed property type: {}", err), true, IkarusErrorInfo_Source_SubSystem, @@ -41,16 +38,17 @@ cppbase::Result IkarusProperty: } IKA_API void ikarus_property_delete(IkarusProperty * property) { - auto * ctx = property->project->get_function_context(); - - TRYRV(, property->project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([ctx](auto const & err) { - ctx->set_error( - fmt::format("failed to delete property from objects table: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - })); + TRYRV( + , + property->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([property](auto const & err) { + property->project->set_error( + fmt::format("failed to delete property from objects table: {}", err), + true, + IkarusErrorInfo_Source_SubSystem, + IkarusErrorInfo_Type_SubSystem_Database + ); + }) + ); property->project->uncache(property); } @@ -60,15 +58,13 @@ IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { } IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { - auto * ctx = property->project->get_function_context(); - VTRYRV( auto const source, nullptr, - property->project->get_db() + property->project->db ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) - .on_error([ctx](auto const & err) { - ctx->set_error( + .on_error([property](auto const & err) { + property->project->set_error( fmt::format("failed to fetch property's source: {}", err), true, IkarusErrorInfo_Source_SubSystem, @@ -81,7 +77,7 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->project->get_blueprint(source)}; case IkarusObjectType_Entity: return new IkarusPropertySource{property->project->get_entity(source)}; default: { - ctx->set_error( + property->project->set_error( fmt::format("PropertySource is neither blueprint nor entity"), true, IkarusErrorInfo_Source_LibIkarus, @@ -94,15 +90,13 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p } IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) { - auto * ctx = property->project->get_function_context(); - VTRYRV( auto const value, nullptr, - property->project->get_db() + property->project->db ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id) - .on_error([ctx](auto const & err) { - ctx->set_error( + .on_error([property](auto const & err) { + property->project->set_error( fmt::format("failed to fetch property's default value: {}", err), true, IkarusErrorInfo_Source_SubSystem, diff --git a/src/objects/util.hpp b/src/objects/util.hpp deleted file mode 100644 index 2779478..0000000 --- a/src/objects/util.hpp +++ /dev/null @@ -1,241 +0,0 @@ -#pragma once - -#include "util.hpp" - -#include - -#include -#include - -#include -#include - -#include - -namespace ikarus::util { - -struct EmptyNameError {}; - -COMPOUND_ERROR(InsertObjectError, EmptyNameError, sqlitecpp::TransactionError); - -template -[[nodiscard]] cppbase::Result, InsertObjectError> insert_object( - IkarusProject * project, - IkarusObjectType type, - std::string_view name, - InsertFunction insert_function, - ObjectFactory object_factory -) { - auto const * object_type_str = ikarus_object_type_to_string(type); - - auto * ctx = project->get_function_context(); - - if (cppbase::is_empty_or_blank(name)) { - ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); - return cppbase::err(EmptyNameError{}); - } - - VTRY( - auto const id, - project->get_db() - ->transact([&](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast(type), name)); - - auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - - TRY(insert_function(db, id)); - - return cppbase::ok(id); - }) - .on_error([&](auto const & err) { - ctx->set_error( - fmt::format("unable to insert {} into database: {}", object_type_str, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - return cppbase::ok(object_factory(id)); -} - -template - requires std::derived_from -void delete_object(Object * object) { - auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - - auto * ctx = object->project->get_function_context(); - - TRYRV(, object->project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const & err) { - ctx->set_error( - fmt::format("failed to delete {} from objects table: {}", object_type_str, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - })); - - object->project->uncache(object); -} - -struct SingleQueryData { - std::string_view table_name; - std::string_view select_field_name; -}; - -template - requires std::derived_from -cppbase::Result fetch_single_field(Object const * object, SingleQueryData const & query_data) { - auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - - auto * ctx = object->project->get_function_context(); - - VTRY( - T value, - object->project->get_db() - ->template query_one( - fmt::format("SELECT `{}` FROM `{}` WHERE `id` = ?", query_data.select_field_name, query_data.table_name), - object->id - ) - .on_error([&](auto const & err) { - ctx->set_error( - fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.select_field_name, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - return value; -} - -struct MultipleBufferQueryData { - std::string_view table_name; - std::string_view select_field_name; - std::string_view where_field_name; - std::string_view relation_desc; -}; - -template - requires std::derived_from -void fetch_multiple_buffered( - Object const * object, - MultipleBufferQueryData const & query_data, - Mapped * mapped_buffer, - size_t buffer_size, - F transformer -) - requires cppbase::is_result_with_value_type_v> -{ - auto * ctx = object->project->get_function_context(); - - auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - - Selected select_buffer[buffer_size]; - - TRYRV( - , - object->project->get_db() - ->template query_many_buffered( - fmt::format( - "SELECT `{}` FROM `{}` WHERE `{}` = ?", - query_data.select_field_name, - query_data.table_name, - query_data.where_field_name - ), - select_buffer, - buffer_size, - object->id - ) - .on_error([&](auto const & err) { - ctx->set_error( - fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.relation_desc, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - for (size_t i = 0; i < buffer_size; ++i) { - VTRYRV(mapped_buffer[i], , transformer(object->project, ctx, select_buffer[i])); - } -} - -struct CountQueryData { - std::string_view table_name; - std::string_view select_field_name; - std::string_view where_field_name; - std::string_view relation_desc; -}; - -template - requires std::derived_from -cppbase::Result fetch_count(Object const * object, CountQueryData const & query_data) { - auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - - auto * ctx = object->project->get_function_context(); - - VTRY( - auto count, - object->project->get_db() - ->template query_one( - fmt::format( - "SELECT COUNT(`{}`) FROM `{}` WHERE `{}` = ?;", - query_data.select_field_name, - query_data.table_name, - query_data.where_field_name - ), - object->id - ) - .on_error([&](auto const & err) { - ctx->set_error( - fmt::format("failed to fetch {} {} count: {}", object_type_str, query_data.relation_desc, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - return cppbase::ok(static_cast(count)); -} - -template -struct ExistsQueryData { - std::string_view table_name; - std::string_view where_field_name; - T where_field_value; - std::string_view relation_desc; -}; - -template - requires std::derived_from -cppbase::Result check_exists(Object const * object, ExistsQueryData const & query_data) { - auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id)); - - auto * ctx = object->project->get_function_context(); - - VTRY( - auto exists, - object->project->get_db() - ->template query_one( - fmt::format("SELECT EXISTS(SELECT 1 FROM `{}` WHERE `{}` = ?);", query_data.table_name, query_data.where_field_name), - query_data.where_field_value - ) - .on_error([&](auto const & err) { - ctx->set_error( - fmt::format("failed to check whether {} {} exists: {}", object_type_str, query_data.relation_desc, err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - return cppbase::ok(static_cast(exists)); -} - -} // namespace ikarus::util diff --git a/src/persistence/function_context.cpp b/src/persistence/function_context.cpp deleted file mode 100644 index f056308..0000000 --- a/src/persistence/function_context.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "function_context.hpp" - -IkarusFunctionContext::IkarusFunctionContext(IkarusProject * project): - _project{project} {} - -IkarusFunctionContext::~IkarusFunctionContext() { - if (_project->_function_contexts.size() == 1) { - if (_project->error_message_buffer.empty()) { - _project->error_message_buffer.push_back('\0'); - } else { - _project->error_message_buffer[0] = '\0'; - } - - _project->error_infos = {}; - } - - _project->_function_contexts.pop_back(); -} diff --git a/src/persistence/function_context.hpp b/src/persistence/function_context.hpp deleted file mode 100644 index 663ff2d..0000000 --- a/src/persistence/function_context.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include - -#include - -#include - -struct IkarusFunctionContext { -public: - explicit IkarusFunctionContext(struct IkarusProject * project); - - IkarusFunctionContext(IkarusFunctionContext const &) noexcept = default; - IkarusFunctionContext(IkarusFunctionContext &&) noexcept = default; - - auto operator=(IkarusFunctionContext const &) noexcept -> IkarusFunctionContext & = default; - auto operator=(IkarusFunctionContext &&) noexcept -> IkarusFunctionContext & = default; - - ~IkarusFunctionContext(); - -public: - template - requires(std::is_same_v && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS) - auto set_error(std::string_view error_message, bool log_error, Infos... infos) -> void { - if (error_message.size() > _project->error_message_buffer.size()) { - _project->error_message_buffer.resize(error_message.size() + 1); - } - - for (int i = 0; i < error_message.size(); ++i) { - _project->error_message_buffer[i] = error_message[i]; - } - - _project->error_message_buffer[error_message.size()] = '\0'; - _project->error_infos = {infos...}; - - if (log_error) { - LOG_ERROR("Error({}): {}", fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), error_message); - } - } - -private: - struct IkarusProject * _project; -}; diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index 0be9991..da56280 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -8,27 +8,14 @@ #include #include #include -#include -auto IkarusProject::get_name() const -> std::string_view { - return _name; -} - -auto IkarusProject::get_path() const -> std::filesystem::path const & { - return _path; -} - -auto IkarusProject::get_db() -> sqlitecpp::Connection * { - return _db.get(); -} - -auto IkarusProject::get_db() const -> sqlitecpp::Connection const * { - return _db.get(); -} - -auto IkarusProject::get_function_context() -> IkarusFunctionContext * { - return &_function_contexts.emplace_back(this); -} +IkarusProject::IkarusProject(std::string_view name, std::filesystem::path path): + name{name}, + path{path}, + db{nullptr}, + _blueprints{}, + _properties{}, + _entities{} {} IkarusBlueprint * IkarusProject::get_blueprint(IkarusId id) { return get_cached_object(id, this->_blueprints); diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index 2359f2e..f113f24 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -11,20 +11,10 @@ #include #include -constexpr inline auto MAXIMUM_ERROR_INFOS = 8; - /// \private struct IkarusProject { public: - [[nodiscard]] auto get_name() const -> std::string_view; - - [[nodiscard]] auto get_path() const -> std::filesystem::path const &; - - [[nodiscard]] auto get_db() -> sqlitecpp::Connection *; - [[nodiscard]] auto get_db() const -> sqlitecpp::Connection const *; - -public: - [[nodiscard]] auto get_function_context() -> struct IkarusFunctionContext *; + IkarusProject(std::string_view name, std::filesystem::path path); public: [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; @@ -53,19 +43,13 @@ private: cache.erase(object->id); } +public: + std::string name; + std::filesystem::path path; + std::unique_ptr db; + private: - friend struct IkarusFunctionContext; - - std::string _name; - std::filesystem::path _path; - std::unique_ptr _db; - - std::array error_infos; - std::string error_message_buffer; - - std::unordered_map> _blueprints; - std::unordered_map> _properties; - std::unordered_map> _entities; - - std::vector _function_contexts; + std::unordered_map> _blueprints; + std::unordered_map> _properties; + std::unordered_map> _entities; }; From 9ad3d62b14dcdd285761bcf5bd4460787473b609 Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 3 Jan 2024 17:14:26 +0100 Subject: [PATCH 118/166] implement remaining logic Signed-off-by: Folling --- .clang-tidy | 6 +- CMakeLists.txt | 2 +- include/ikarus/errors.h | 87 +++-- include/ikarus/objects/entity.h | 51 ++- include/ikarus/objects/object.h | 16 +- include/ikarus/objects/object_type.h | 3 +- .../objects/properties/number_property.h | 4 +- include/ikarus/objects/properties/property.h | 2 +- .../ikarus/objects/properties/property_type.h | 6 +- .../ikarus/objects/properties/text_property.h | 4 +- .../objects/properties/toggle_property.h | 4 +- include/ikarus/persistence/project.h | 68 +--- include/ikarus/values/entity_property_value.h | 46 +++ include/ikarus/values/value.h | 6 +- src/CMakeLists.txt | 2 + src/errors.cpp | 64 ++-- src/errors.hpp | 93 ++++++ src/objects/blueprint.cpp | 174 +++++----- src/objects/entity.cpp | 297 ++++++++++++++++-- src/objects/object.cpp | 104 ++++++ src/objects/object.hpp | 4 - src/objects/properties/number_property.cpp | 29 ++ src/objects/properties/number_property.hpp | 11 +- src/objects/properties/property.cpp | 131 ++++---- src/objects/properties/property.hpp | 17 +- src/objects/properties/property_source.cpp | 12 + src/objects/properties/property_source.hpp | 5 +- src/objects/properties/text_property.cpp | 29 ++ src/objects/properties/text_property.hpp | 11 +- src/objects/properties/toggle_property.cpp | 30 +- src/objects/properties/toggle_property.hpp | 7 + src/objects/properties/util.hpp | 100 ++++++ src/persistence/migrations.hpp | 40 +++ src/persistence/migrations/m0_genesis.sql | 7 + src/persistence/project.cpp | 233 +++++++++++++- src/persistence/project.hpp | 18 +- src/values/entity_property_value.cpp | 21 ++ src/values/entity_property_value.hpp | 7 + src/values/value.cpp | 2 +- src/values/value.hpp | 46 ++- vendor/sqlitecpp | 2 +- 41 files changed, 1393 insertions(+), 408 deletions(-) create mode 100644 include/ikarus/values/entity_property_value.h create mode 100644 src/errors.hpp create mode 100644 src/objects/properties/util.hpp create mode 100644 src/persistence/migrations.hpp create mode 100644 src/persistence/migrations/m0_genesis.sql create mode 100644 src/values/entity_property_value.cpp create mode 100644 src/values/entity_property_value.hpp diff --git a/.clang-tidy b/.clang-tidy index c2cf005..63731e9 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,7 +1,7 @@ Checks: >- -*, bugprone-*, -bugprone-lambda-function-name, - cppcoreguidelines-*, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes, + cppcoreguidelines-*, -cppcoreguidelines-macro-usage, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes, clang-analyzer-*, google-*, -google-readability-todo, modernize-*, -modernize-use-trailing-return-type, @@ -9,9 +9,9 @@ Checks: >- portability-*, readability-*, -readability-redundant-access-specifiers CheckOptions: - readability-identifier-length.IgnoredParameterNames: '^(db|rc|id)$' + readability-identifier-length.IgnoredParameterNames: '^(db|rc|id|ec)$' readability-identifier-length.IgnoredLoopCounterNames: '^[ij]$' - readability-identifier-length.IgnoredVariableNames: '^(db|rc|id)$' + readability-identifier-length.IgnoredVariableNames: '^(db|rc|id|ec)$' cppcoreguidelines-avoid-do-while.IgnoreMacros: Yes HeaderFileExtensions: - h diff --git a/CMakeLists.txt b/CMakeLists.txt index ae81667..8389d4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ add_subdirectory(vendor) add_subdirectory(include) add_subdirectory(src) -find_package(Boost REQUIRED) +find_package(Boost COMPONENTS system filesystem REQUIRED) add_library( libikarus SHARED diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index 92b16cf..a4887a3 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -4,6 +4,7 @@ /// \author Folling #include +#include /// \addtogroup errors Errors /// \brief Error handling within libikarus @@ -24,94 +25,81 @@ IKARUS_BEGIN_HEADER enum IkarusErrorInfo { /// \brief No error occurred. IkarusErrorInfo_None = 0x0, - /// \brief The error was caused by the client. - IkarusErrorInfo_Client = 0x10100000, - /// \brief The error was caused by a dependency (e.g. boost) of libikarus. - IkarusErrorInfo_Dependency = 0x10200000, - /// \brief The error was caused by the filesystem. - IkarusErrorInfo_Filesystem = 0x10300000, - /// \brief The error was caused by the database. - IkarusErrorInfo_Database = 0x10400000, - /// \brief The error was caused by the underlying OS. - IkarusErrorInfo_OS = 0x10500000, - /// \brief The error was caused by libikarus itself. - IkarusErrorInfo_LibIkarus = 0x10600000, - /// \brief The client misused the API. /// Example: Accessing a resource that does not exist. - IkarusErrorInfo_Client_Misuse = 0x10100001, + IkarusErrorInfo_Client_Misuse = 0x01000001, /// \brief The client provided a null value for a parameter that must not be null. /// Example: Passing null for `ikarus_project_get_name` - IkarusErrorInfo_Client_InvalidNull = 0x10100002, + IkarusErrorInfo_Client_InvalidNull = 0x01000002, /// \brief The client provided an index that was out of bounds for some array. /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3. - IkarusErrorInfo_Client_IndexOutOfBounds = 0x10100003, + IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000003, /// \brief The client provided a numeric value that was out of bounds /// Example: Passing the value 2^32 to an i32 (might be passed as a string). - IkarusErrorInfo_Client_ValueOutOfBounds = 0x10100004, + IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000004, /// \brief The client provided invalid input that doesn't fit in any of the other categories. /// Example: Passing an empty/blank string for a string that must be non-empty/-blank. - IkarusErrorInfo_Client_InvalidInput = 0x10100005, + IkarusErrorInfo_Client_InvalidInput = 0x01000005, /// \brief The client provided valid data in an invalid format. /// Example: Passing a malformed JSON string. - IkarusErrorInfo_Client_InvalidFormat = 0x10100006, + IkarusErrorInfo_Client_InvalidFormat = 0x01000006, /// \brief The client violated a constraint. /// \details This error is most likely caused by endusers. /// Example: A user tries to set the age of a character to an value outside of their specified range. - IkarusErrorInfo_Client_ConstraintViolated = 0x10100007, + IkarusErrorInfo_Client_ConstraintViolated = 0x10000007, + + // 0x02 reserved for dependency errors - /// \brief A file was not found. - IkarusErrorInfo_Filesystem_NotFound = 0x10300001, /// \brief A file or directory already exists. - IkarusErrorInfo_Filesystem_AlreadyExists = 0x10300002, + IkarusErrorInfo_Filesystem_AccessIssue = 0x03000001, + /// \brief A file was not found. + IkarusErrorInfo_Filesystem_NotFound = 0x03000002, + /// \brief A file or directory already exists. + IkarusErrorInfo_Filesystem_AlreadyExists = 0x03000003, /// \brief Missing permissions to access a file or directory. - IkarusErrorInfo_Filesystem_MissingPermissions = 0x10300003, + IkarusErrorInfo_Filesystem_MissingPermissions = 0x03000004, /// \brief Insufficient space to perform an operation. - IkarusErrorInfo_Filesystem_InsufficientSpace = 0x10300004, + IkarusErrorInfo_Filesystem_InsufficientSpace = 0x03000005, /// \brief A path is invalid. - IkarusErrorInfo_Filesystem_InvalidPath = 0x10300005, + IkarusErrorInfo_Filesystem_InvalidPath = 0x03000006, /// \brief A database connection failed. - IkarusErrorInfo_Database_ConnectionFailed = 0x10400001, + IkarusErrorInfo_Database_ConnectionFailed = 0x04000001, /// \brief A database query failed. - IkarusErrorInfo_Database_QueryFailed = 0x10400002, + IkarusErrorInfo_Database_QueryFailed = 0x04000002, /// \brief A database migration failed. - IkarusErrorInfo_Database_MigrationFailed = 0x10400003, + IkarusErrorInfo_Database_MigrationFailed = 0x04000003, /// \brief A database is in an invalid state. This indicates a corrupt project. /// Example: An entity is linked to a non-existant blueprint. - IkarusErrorInfo_Database_InvalidState = 0x10400004, + IkarusErrorInfo_Database_InvalidState = 0x04000004, /// \brief A system call failed. - IkarusErrorInfo_OS_SystemCallFailed = 0x10500001, + IkarusErrorInfo_OS_SystemCallFailed = 0x05000001, /// \brief A system call returned an invalid value. - IkarusErrorInfo_OS_InvalidReturnValue = 0x10500002, + IkarusErrorInfo_OS_InvalidReturnValue = 0x05000002, /// \brief An OOM error occurred. - IkarusErrorInfo_OS_InsufficientMemory = 0x10500003, + IkarusErrorInfo_OS_InsufficientMemory = 0x05000003, /// \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, + /// whereas the former may imply that the state is ephemeral, e.g. data within a function. /// Example: The name of an object is found to be invalid UTF8. - IkarusErrorInfo_LibIkarus_InvalidState = 0x20030001, + IkarusErrorInfo_LibIkarus_InvalidState = 0x06000001, + /// \brief libikarus is unable to perform a certain operation that should succeed. + IkarusErrorInfo_LibIkarus_CannotPerformOperation = 0x06000002, /// \brief libikarus is unable to perform a certain operation within a given timeframe. /// Example: A query takes longer than the timeout. - IkarusErrorInfo_LibIkarus_Timeout = 0x20030003, + IkarusErrorInfo_LibIkarus_Timeout = 0x06000003, }; -/// \brief The data limits for an error. -enum IkarusErrorDataLimit { - /// \brief The maximum number of error infos that can be stored in an error. - IkarusErrorDataLimit_MaxErrorInfos = 8, - /// \brief The maximum length of an error message. - IkarusErrorDataLimit_MaxMessageLength = 128, -}; +size_t const IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT = 128; /// \brief The data stored for an error struct IkarusErrorData { - /// \brief The scope of the error. - /// \details This array may at most hold #IkarusErrorDataLimit_MaxErrorInfos elements. - /// The first occurrence of #IkarusErrorInfo_None signifies the end of the array. If this happens at idx x== 0, no error occurred. - IkarusErrorInfo infos[IkarusErrorDataLimit_MaxErrorInfos]; + /// \brief The error type + IkarusErrorInfo info; - char message[IkarusErrorDataLimit_MaxMessageLength]; + char message[IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT]; }; /// \brief Gets the name of an error info. @@ -128,11 +116,6 @@ IKA_API bool ikarus_error_data_is_success(IkarusErrorData const * data); /// \param data The error data to check. /// \return True if the error data is an error, false otherwise. IKA_API bool ikarus_error_data_is_error(IkarusErrorData const * data); -/// \brief Formats the error data in a reasonable but unspecified way. -/// \param data The error data to format. -/// \return The formatted error data. -/// \remark Ownership of the returned pointer is passed to the user and must be freed at their leisure using ikarus_free. -IKA_API char const * ikarus_error_data_pretty_format(IkarusErrorData const * data); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index cfc09f3..9610230 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -89,6 +89,30 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru /// \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 the blueprints an entity is linked to. +/// \param entity The entity to get the blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprints_out The buffer to write the blueprints to. +/// \pre \li Must not be null. +/// \param blueprints_out_size The size of the buffer. +/// \param error_out \see errors.h +/// \see ikarus_entity_get_linked_blueprint_count +IKA_API void ikarus_entity_get_linked_blueprints( + IkarusEntity const * entity, + struct IkarusBlueprint ** blueprints_out, + size_t blueprints_out_size, + IkarusErrorData * error_out +); + +/// \brief Gets the number of blueprints an entity is linked to. +/// \param entity The entity to get the number of blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The number of blueprints or undefined if an error occurs. +IKA_API size_t ikarus_entity_get_linked_blueprint_count(IkarusEntity const * entity, IkarusErrorData * error_out); + /// \brief Checks if an entity has a specific property. /// \param entity The entity to check. /// \pre \li Must not be null. @@ -97,17 +121,9 @@ IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct I /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return True if the entity has the property, false otherwise. +/// \return False if an error occurs or the entity does not have the property, true otherwise. IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); -/// \brief Gets the number of properties of an entity. -/// \param entity The entity to get the number of properties of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity, IkarusErrorData * error_out); - /// \brief Gets the properties of an entity. /// \param entity The entity to get the properties of. /// \pre \li Must not be null. @@ -116,6 +132,7 @@ IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity, Ika /// \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, @@ -123,9 +140,16 @@ IKA_API void ikarus_entity_get_properties( IkarusErrorData * error_out ); +/// \brief Gets the number of properties of an entity. +/// \param entity The entity to get the number of properties of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The number of properties or undefined if an error occurs. +IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity, IkarusErrorData * error_out); + /// \brief Gets the value of a property of an entity. -/// \details If the entity has never set the value of the property, the default value is returned (which may be -/// undefined). +/// \details If the entity has never set the value of the property, the default value is returned (which may be undefined). /// \param entity The entity to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -134,9 +158,8 @@ IKA_API void ikarus_entity_get_properties( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The value of the property or null if the entity does not have the property or an error occurs. -/// \remark Must be freed using -/// #ikarus_free. -IKA_API struct IkarusEntityValue * +/// \remark Must be freed using #ikarus_free. +IKA_API struct IkarusEntityPropertyValue * ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Sets the value of a property of an entity. diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index 2aa3d0c..9dd3b39 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -19,6 +19,13 @@ IKARUS_BEGIN_HEADER /// \brief A generic object. Wraps all types of objects, including folders. struct IkarusObject; +/// \brief Fetches the project of an object. +/// \param object The object to fetch the project from. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return The project of the object or null if an error occurs. +IKA_API struct IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErrorData * error_out); + /// \brief Compares two objects for equality. /// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the /// same object. @@ -35,9 +42,6 @@ IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const /// \param blueprint_visitor The function to call if the object is a blueprint. Skipped if null. /// \param property_visitor The function to call if the object is a property. Skipped if null. /// \param entity_visitor The function to call if the object is an entity. Skipped if null. -/// \param blueprint_folder_visitor The function to call if the object is a blueprint folder. Skipped if null. -/// \param property_folder_visitor The function to call if the object is a property folder. Skipped if null. -/// \param entity_folder_visitor The function to call if the object is an entity folder. Skipped if null. /// \param data The data passed to the visitor functions. /// \param error_out \see errors.h IKA_API void ikarus_object_visit( @@ -45,9 +49,6 @@ IKA_API void ikarus_object_visit( void (*blueprint_visitor)(struct IkarusBlueprint *, void *), void (*property_visitor)(struct IkarusProperty *, void *), void (*entity_visitor)(struct IkarusEntity *, void *), - void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder *, void *), - void (*property_folder_visitor)(struct IkarusPropertyFolder *, void *), - void (*entity_folder_visitor)(struct IkarusEntityFolder *, void *), void * data, IkarusErrorData * error_out ); @@ -58,9 +59,6 @@ IKA_API void ikarus_object_visit_const( void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), void (*property_visitor)(struct IkarusProperty const *, void *), void (*entity_visitor)(struct IkarusEntity const *, void *), - void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder const *, void *), - void (*property_folder_visitor)(struct IkarusPropertyFolder const *, void *), - void (*entity_folder_visitor)(struct IkarusEntityFolder const *, void *), void * data, IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h index 7dd3507..e30bb3c 100644 --- a/include/ikarus/objects/object_type.h +++ b/include/ikarus/objects/object_type.h @@ -25,10 +25,9 @@ enum IkarusObjectType { /// \brief Converts an IkarusObjectType to a string. /// \param type The type to convert. -/// \param error_out \see errors.h /// \return The string representation of the type. /// \remark The returned string must not be freed. -char const * ikarus_object_type_to_string(IkarusObjectType type, IkarusErrorData * error_out); +char const * ikarus_object_type_to_string(IkarusObjectType type); IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 5d24f87..8ca4ffd 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -46,7 +46,7 @@ ikarus_number_property_get_default_value(struct IkarusNumberProperty * property, /// \param property The number property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param default_value The default value. +/// \param new_default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. /// \param error_out \see errors.h @@ -54,7 +54,7 @@ ikarus_number_property_get_default_value(struct IkarusNumberProperty * property, /// default values and other settings. IKA_API void ikarus_number_property_set_default_value( struct IkarusNumberProperty * property, - struct IkarusNumberValue * default_value, + struct IkarusNumberValue * new_default_value, IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index 34f2d9a..7a94079 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -89,7 +89,7 @@ IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusPro /// \param error_out \see errors.h /// \return The default value of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out); +IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Visits a property. Calling the appropriate function for the property's type. /// \param property The property to visit. diff --git a/include/ikarus/objects/properties/property_type.h b/include/ikarus/objects/properties/property_type.h index ee6ec19..893cdca 100644 --- a/include/ikarus/objects/properties/property_type.h +++ b/include/ikarus/objects/properties/property_type.h @@ -15,11 +15,11 @@ IKARUS_BEGIN_HEADER /// available. enum IkarusPropertyType { /// \brief A true/false boolean-esque value. - IkarusPropertyType_Toggle, + IkarusPropertyType_Toggle = 0x10000000, /// \brief A numeric value, limited to IEEE 80 bit floating point numbers. - IkarusPropertyType_Number, + IkarusPropertyType_Number = 0x20000000, /// \brief An arbitrary UTF-8 textual value. - IkarusPropertyType_Text, + IkarusPropertyType_Text = 0x30000000, }; IKARUS_END_HEADER diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index 0d240b2..0408614 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -45,7 +45,7 @@ IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct I /// \param property The text property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param default_value The default value. +/// \param new_default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. /// \param error_out \see errors.h @@ -53,7 +53,7 @@ IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct I /// default values and other settings. IKA_API void ikarus_text_property_set_default_value( struct IkarusTextProperty * property, - struct IkarusTextValue * default_value, + struct IkarusTextValue * new_default_value, IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index e084123..892d418 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -46,7 +46,7 @@ ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, /// \param property The toggle property. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param default_value The default value. +/// \param new_default_value The default value. /// \pre \li Must not be null. /// \pre \li Must be a valid value for the property. /// \param error_out \see errors.h @@ -54,7 +54,7 @@ ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, /// default values and other settings. IKA_API void ikarus_toggle_property_set_default_value( struct IkarusToggleProperty * property, - struct IkarusToggleValue * default_value, + struct IkarusToggleValue * new_default_value, IkarusErrorData * error_out ); diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 93c4e11..c4406f8 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -51,39 +51,13 @@ IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name, Ikaru /// ikarus_project_delete IKA_API IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_out); -/// \brief Copies a project to a new location. -/// \details The new project is not opened. -/// \param project The project to copy. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param target_path The new location of the project. -/// \pre \li Must not be null. -/// \pre \li Must point to a valid unused path on the system. -/// \param target_name The name of the new project. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param error_out \see errors.h -/// \remark If successful the project connection remains intact. The previous location will still exist. -IKA_API void -ikarus_project_copy(IkarusProject const * project, char const * target_path, char const * target_name, IkarusErrorData * error_out); - -/// \brief Deletes a project and all its associated data from the filesystem. -/// \param project The project to delete. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \remark also frees the project. -/// \remark In-Memory projects will just be freed. -/// \remark If deletion fails, the project pointer remains intact. -IKA_API void ikarus_project_delete(IkarusProject * project, IkarusErrorData * error_out); - /// \brief Gets the name of a project. /// \param project The project to get the name of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The name of the project. -/// \remark Must be freed using #ikarus_free. +/// \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. @@ -102,30 +76,9 @@ IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_n /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The path of the project. -/// \remark Must be freed using #ikarus_free. +/// \remark Ownership remains with libikarus, must not be freed. IKA_API char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out); -/// \brief Moves a project to a new location. -/// \param project The project to move. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param target_path The new location of the project. -/// \pre \li Must not be null. -/// \pre \li Must point to a valid unused path on the system. -/// \param error_out \see errors.h -/// \remark If successful the project connection remains intact. The previous location will not exist anymore. -/// \remark Due to the nature of filesystems this function may not be atomic. -IKA_API void ikarus_project_move(IkarusProject * project, char const * target_path, IkarusErrorData * error_out); - -/// \brief Gets the blueprint root folder of a project. -/// \param project The project to get the blueprint root folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The blueprint root folder of the project or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder(IkarusProject const * project, IkarusErrorData * error_out); - /// \brief Gets the blueprints of a project. /// \param project The project to get the blueprints of. /// \pre \li Must not be null. @@ -135,7 +88,7 @@ IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder( /// \param blueprints_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_blueprints( - IkarusProject const * project, + IkarusProject * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size, IkarusErrorData * error_out @@ -147,16 +100,7 @@ IKA_API void ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); - -/// \brief Gets the entity root folder of a project. -/// \param project The project to get the entity root folder of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The entity root folder of the project or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(IkarusProject const * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject * project, IkarusErrorData * error_out); /// \brief Gets the entities of a project. /// \param project The project to get the entities of. @@ -167,7 +111,7 @@ IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(Ikarus /// \param entities_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_entities( - IkarusProject const * project, + IkarusProject * project, struct IkarusEntity ** entities_out, size_t entities_out_size, IkarusErrorData * error_out @@ -179,7 +123,7 @@ IKA_API void ikarus_project_get_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of entities or undefined if an error occurs. -IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/entity_property_value.h b/include/ikarus/values/entity_property_value.h new file mode 100644 index 0000000..c408f00 --- /dev/null +++ b/include/ikarus/values/entity_property_value.h @@ -0,0 +1,46 @@ +#pragma once + +/// \file entity_property_value.h +/// \author Folling + +/// \defgroup entity_property_values EntityPropertyValue +/// \brief Values in relation to an entity and one of its properties. +/// @{ + +#include +#include +#include + +IKARUS_BEGIN_HEADER + +/// \brief Like an \ref value.h "IkarusValue", but in relation to an entity and one of its properties +struct IkarusEntityPropertyValue; + +/// \brief Fetches the entity of an entity property value. +/// \param value The entity property value. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return The entity of the entity property value. +/// \remark This value is owned by the entity property value and must not be freed directly. +struct IkarusEntity const * ikarus_entity_property_value_get_entity(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); + +/// \brief Fetches the property of an entity property value. +/// \param value The entity property value. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return The property of the entity property value. +/// \remark This value is owned by the entity property value and must not be freed directly. +struct IkarusProperty const * +ikarus_entity_property_value_get_property(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); + +/// \brief Fetches the value of an entity property value. +/// \param value The entity property value. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return The value of the entity property value. +/// \remark This value is owned by the entity property value and must not be freed directly. +struct IkarusValue const * ikarus_entity_property_value_get_value(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); + +IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index 3fcaf63..fbc50e1 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -3,9 +3,6 @@ /// \file value.h /// \author Folling -#include -#include - /// \defgroup values Values /// \brief The values of properties. /// \details Each entity has a value for each property it is associated with. @@ -17,6 +14,9 @@ /// property's settings. \see PropertyType /// @{ +#include +#include + IKARUS_BEGIN_HEADER /// \brief The common type for all values. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b00545..0963d2e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,8 @@ file( FILES "*.hpp" "*.cpp" + "*.h" + "*.c" ) set(SOURCE_FILES ${FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/errors.cpp b/src/errors.cpp index 32b2c99..4441fe9 100644 --- a/src/errors.cpp +++ b/src/errors.cpp @@ -11,61 +11,39 @@ char const * get_error_info_name(IkarusErrorInfo info) { switch (info) { case IkarusErrorInfo_None: return "None"; - case IkarusErrorInfo_Client: return "Client"; - case IkarusErrorInfo_Dependency: return "Dependency"; - case IkarusErrorInfo_Filesystem: return "Filesystem"; - case IkarusErrorInfo_Database: return "Database"; - case IkarusErrorInfo_OS: return "OS"; - case IkarusErrorInfo_LibIkarus: return "libikarus"; - case IkarusErrorInfo_Client_Misuse: return "Misuse"; - case IkarusErrorInfo_Client_InvalidInput: return "InvalidInput"; - case IkarusErrorInfo_Client_InvalidFormat: return "InvalidFormat"; - case IkarusErrorInfo_Client_ConstraintViolated: return "ConstraintViolated"; + case IkarusErrorInfo_Client_Misuse: return "Client::Misuse"; + case IkarusErrorInfo_Client_InvalidInput: return "Client::InvalidInput"; + case IkarusErrorInfo_Client_InvalidFormat: return "Client::InvalidFormat"; + case IkarusErrorInfo_Client_ConstraintViolated: return "Client::ConstraintViolated"; - case IkarusErrorInfo_Filesystem_NotFound: return "NotFound"; - case IkarusErrorInfo_Filesystem_AlreadyExists: return "AlreadyExists"; - case IkarusErrorInfo_Filesystem_MissingPermissions: return "MissingPermissions"; - case IkarusErrorInfo_Filesystem_InsufficientSpace: return "InsufficientSpace"; - case IkarusErrorInfo_Filesystem_InvalidPath: return "InvalidPath"; + case IkarusErrorInfo_Filesystem_NotFound: return "Filesystem::NotFound"; + case IkarusErrorInfo_Filesystem_AlreadyExists: return "Filesystem::AlreadyExists"; + case IkarusErrorInfo_Filesystem_MissingPermissions: return "Filesystem::MissingPermissions"; + case IkarusErrorInfo_Filesystem_InsufficientSpace: return "Filesystem::InsufficientSpace"; + case IkarusErrorInfo_Filesystem_InvalidPath: return "Filesystem::InvalidPath"; - case IkarusErrorInfo_Database_ConnectionFailed: return "ConnectionFailed"; - case IkarusErrorInfo_Database_QueryFailed: return "QueryFailed"; - case IkarusErrorInfo_Database_MigrationFailed: return "MigrationFailed"; - case IkarusErrorInfo_Database_InvalidState: return "InvalidState"; + case IkarusErrorInfo_Database_ConnectionFailed: return "Database::ConnectionFailed"; + case IkarusErrorInfo_Database_QueryFailed: return "Database::QueryFailed"; + case IkarusErrorInfo_Database_MigrationFailed: return "Database::MigrationFailed"; + case IkarusErrorInfo_Database_InvalidState: return "Database::InvalidState"; - case IkarusErrorInfo_OS_SystemCallFailed: return "SystemCallFailed"; - case IkarusErrorInfo_OS_InvalidReturnValue: return "InvalidReturnValue"; - case IkarusErrorInfo_OS_InsufficientMemory: return "InsufficientMemory"; + case IkarusErrorInfo_OS_SystemCallFailed: return "OS::SystemCallFailed"; + case IkarusErrorInfo_OS_InvalidReturnValue: return "OS::InvalidReturnValue"; + case IkarusErrorInfo_OS_InsufficientMemory: return "OS::InsufficientMemory"; - case IkarusErrorInfo_LibIkarus_InvalidState: return "InvalidState"; - case IkarusErrorInfo_LibIkarus_Timeout: return "Timeout"; + case IkarusErrorInfo_LibIkarus_InvalidState: return "LibIkarus::InvalidState"; + case IkarusErrorInfo_LibIkarus_CannotPerformOperation: return "LibIkarus::CannotPerformOperation"; + case IkarusErrorInfo_LibIkarus_Timeout: return "LibIkarus::Timeout"; default: return "Invalid"; } } bool ikarus_error_data_is_success(IkarusErrorData const * data) { - return data->infos[0] == IkarusErrorInfo_None; + return data->info == IkarusErrorInfo_None; } bool ikarus_error_data_is_error(IkarusErrorData const * data) { - return data->infos[0] != IkarusErrorInfo_None; -} - -char const * ikarus_error_data_pretty_format(IkarusErrorData const * data) { - if (ikarus_error_data_is_success(data)) { - return "Success"; - } - - auto const formatted = fmt::format( - "{} - {}", - fmt::join( - data->infos | std::views::take_while(cppbase::pred_ne(IkarusErrorInfo_None)) | std::views::transform(get_error_info_name), - "->" - ), - data->message - ); - - return strndup(formatted.data(), formatted.size()); + return data->info != IkarusErrorInfo_None; } diff --git a/src/errors.hpp b/src/errors.hpp new file mode 100644 index 0000000..0a9941f --- /dev/null +++ b/src/errors.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include + +#include + +inline void safe_strcpy(char * dest, std::string_view src, size_t dest_size) { + for (int i = 0; i < dest_size; ++i) { + if (src[i] == '\0') { + dest[i] = '\0'; + return; + } + + dest[i] = src[i]; + } +} + +#define IKARUS_SET_ERROR(msg, err_info) \ + if (error_out != nullptr) { \ + safe_strcpy(static_cast(error_out->message), msg, IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT); \ + error_out->info = err_info; \ + } + +#define IKARUS_FAIL(ret, msg, err_info) \ + IKARUS_SET_ERROR(msg, err_info); \ + return ret + +#define IKARUS_FAIL_IF(condition, ret, msg, err_info) \ + if (condition) { \ + IKARUS_SET_ERROR(msg, err_info) \ + return ret; \ + } + +#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_TRY_OR_FAIL_IMPL(var_name, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ + return var_name; \ + } + +#define IKARUS_TRY_OR_FAIL(msg, err_info, ...) IKARUS_TRY_OR_FAIL_IMPL(CPPBASE_UNIQUE_NAME(result), msg, err_info, __VA_ARGS__); + +#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + 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, ...) \ + 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, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ + return var_name; \ + } \ + value = var_name.unwrap_value() + +#define IKARUS_VTRY_OR_FAIL(value, msg, err_info, ...) \ + 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, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ + return ret; \ + } \ + value = var_name.unwrap_value() + +#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__); + +#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("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); diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index 117cbfc..971bd13 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -1,11 +1,13 @@ #include "ikarus/objects/blueprint.h" +#include "ikarus/objects/properties/property.h" #include "objects/blueprint.hpp" #include #include #include +#include #include #include #include @@ -13,117 +15,149 @@ IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusObject{project, id} {} -IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) { - if (cppbase::is_empty_or_blank(name)) { - project->set_error("blueprint name must not be empty", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input); +IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), nullptr, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); - return nullptr; - } - - project->db - ->transact([name](auto * db) { + IKARUS_VTRYRV_OR_FAIL( + IkarusId const id, + nullptr, + "failed to create blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->transact([name](auto * db) -> cppbase::Result { TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Blueprint, name)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id)); - return cppbase::ok(); + return cppbase::ok(id); }) - .on_error([project](auto const & err) { - project->set_error( - fmt::format("failed to create blueprint: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }); + ); + + return project->get_blueprint(id); } -void ikarus_blueprint_delete(IkarusBlueprint * blueprint) { - blueprint->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id).on_error([blueprint](auto const & err) { - blueprint->project->set_error( - fmt::format("failed to delete blueprint from objects table: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }); +void ikarus_blueprint_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 `objects` WHERE `id` = ?", blueprint->id) + ); + + blueprint->project->uncache(blueprint); } void ikarus_blueprint_get_properties( IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, - size_t properties_out_size + size_t properties_out_size, + IkarusErrorData * error_out ) { - IkarusId ids[properties_out_size]; + IKARUS_FAIL_IF_NULL(blueprint, ); + IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, ); + IKARUS_FAIL_IF_NULL(properties_out, ); - TRYRV( + if (properties_out_size == 0) { + return; + } + + std::tuple ids_and_types[properties_out_size]; + + IKARUS_TRYRV_OR_FAIL( , - blueprint->project->db - ->query_many_buffered("SELECT `id` FROM `properties` WHERE `source` = ?", ids, properties_out_size, blueprint->id) - .on_error([&](auto const & err) { - blueprint->project->set_error( - fmt::format("failed to fetch blueprint properties from database: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); + "unable to fetch blueprint properties from database: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_many_buffered( + "SELECT `id` FROM `properties` WHERE `source` = ?", + ids_and_types, + properties_out_size, + blueprint->id + ) + ) - // not atomic, could be switched to two loops if necessary for (size_t i = 0; i < properties_out_size; ++i) { - IkarusId id = ids[i]; - - VTRYRV(auto const type, , IkarusProperty::get_property_type(blueprint->project, id)); + 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) { - return blueprint->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", blueprint->id) - .unwrap_value_or(0); +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("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", blueprint->id) + ); + + return ret; } void ikarus_blueprint_get_linked_entities( IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, - size_t entities_out_size + 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; + } + IkarusId ids[entities_out_size]; - TRYRV( + IKARUS_TRYRV_OR_FAIL( , - blueprint->project->db - ->query_many_buffered( - "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", - ids, - entities_out_size, - blueprint->id - ) - .on_error([&](auto const & err) { - blueprint->project->set_error( - fmt::format("failed to fetch linked entities from database: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); + "unable to fetch blueprint linked entities from database: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_many_buffered( + "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) { - return blueprint->project->db->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) - .unwrap_value_or(0); +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("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) + ); + + return ret; } -IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint) { +// No existence checks here for performance reasons. All methods on IkarusObject perform this check anyway. + +IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(blueprint, nullptr); + return static_cast(blueprint); } -IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint) { +IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(blueprint, nullptr); + return static_cast(blueprint); } diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp index af2395f..174123e 100644 --- a/src/objects/entity.cpp +++ b/src/objects/entity.cpp @@ -1,36 +1,289 @@ #include "entity.hpp" +#include "values/value.hpp" + #include +#include #include -#include +#include #include +#include -IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name) { - return ikarus::util::insert_object( - project, - IkarusObjectType_Entity, - name, - [](auto * db, IkarusId id) { return db->execute("INSERT INTO `entities`(`id`) VALUES(?)", id); }, - [project](IkarusId id) { return project->get_entity(id); } - ).unwrap_value_or(nullptr); +IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + 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( + IkarusId const id, + nullptr, + "failed to create entity: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->transact([name](auto * db) -> cppbase::Result { + TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Entity, name)); + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Entity); + TRY(db->execute("INSERT INTO `entities`(`id`) VALUES(?)", id)); + return cppbase::ok(id); + }) + ); + + return project->get_entity(id); } -void ikarus_entity_delete(IkarusEntity * entity) { - ikarus::util::delete_object(entity); +void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(entity, ); + IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); + + IKARUS_TRYRV_OR_FAIL( + , + "unable to delete entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute("DELETE FROM `objects` WHERE `id` == ?", entity->id) + ); + + entity->project->uncache(entity); } -bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint) { - return ikarus::util::check_exists( - entity, - ikarus::util::ExistsQueryData{ - .table_name = "entity_blueprint_links", - .where_field_name = "blueprint", - .where_field_value = blueprint->id, - .relation_desc = "linked blueprints" - } +bool ikarus_entity_is_linked_to_blueprint( + IkarusEntity const * entity, + struct IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, false); + IKARUS_FAIL_IF_OBJECT_MISSING(entity, false); + 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( + "SELECT EXISTS(SELECT 1 FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?)", + entity->id, + blueprint->id + ) ) - .unwrap_value_or(false); + + return ret; } -bool ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint) {} +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) + ); +} + +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; + } + + IkarusId 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( + "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("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", entity->id) + ); + + return ret; +} + +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); + + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + false, + "unable to check whether entity has property: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT EXISTS(SELECT 1 FROM `entity_properties` WHERE `entity` = ? AND `property` = ?)", + entity->id, + property->id + ) + ) + + return ret; +} + +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 ids_and_types[properties_out_size]; + + IKARUS_TRYRV_OR_FAIL( + , + "unable to fetch entity properties from database: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_many_buffered( + "SELECT `property`, `type` FROM `properties` WHERE `source` = ?", + 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); + + IKARUS_VTRYRV_OR_FAIL( + size_t const ret, + 0, + "unable to fetch entity property count from database: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", entity->id) + ); + + return ret; +} + +struct IkarusEntityPropertyValue * +ikarus_entity_get_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((SELECT `value` FROM `values` WHERE `entity` = ? AND `property` = ?), (SELECT `default_value` FROM `properties` WHERE `id` = ?))", + entity->id, + property->id, + property->id + ); + + IKARUS_FAIL_IF_ERROR(nullptr); + + return new IkarusEntityPropertyValue{ + .entity = entity, + .property = property, + .value = value, + }; +} + +void ikarus_entity_set_value( + IkarusEntity * entity, + struct IkarusProperty const * property, + struct IkarusEntityPropertyValue * 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, ); + + auto value_json_str = boost::json::serialize(value->value->to_json()); + + IKARUS_TRYRV_OR_FAIL( + , + "unable to set entity property value: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "INSERT INTO `values`(`entity`, `property`, `value`) VALUES(?, ?, ?) ON CONFLICT(`entity`, `property`) DO UPDATE SET `value` = ?", + entity->id, + property->id, + value_json_str, + value_json_str + ) + ); +} + +// No existence checks here for performance reasons. All methods on IkarusObject perform this check anyway. + +struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + + return static_cast(entity); +} + +struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + + return static_cast(entity); +} diff --git a/src/objects/object.cpp b/src/objects/object.cpp index 204c7a7..2f52b48 100644 --- a/src/objects/object.cpp +++ b/src/objects/object.cpp @@ -1,5 +1,109 @@ #include "object.hpp" +#include + +#include + +#include +#include +#include +#include +#include + IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): project{project}, id{id} {} + +IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(object, nullptr); + IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); + + return object->project; +} + +bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(lhs, false); + IKARUS_FAIL_IF_OBJECT_MISSING(lhs, false); + IKARUS_FAIL_IF_NULL(rhs, false); + IKARUS_FAIL_IF_OBJECT_MISSING(rhs, false); + + return lhs->id == rhs->id; +} + +void ikarus_object_visit( + IkarusObject * object, + void (*blueprint_visitor)(struct IkarusBlueprint *, void *), + void (*property_visitor)(struct IkarusProperty *, void *), + void (*entity_visitor)(struct IkarusEntity *, void *), + void * data, + IkarusErrorData * error_out +) { + struct Data { + void (*blueprint_visitor)(struct IkarusBlueprint *, void *); + void (*property_visitor)(struct IkarusProperty *, void *); + void (*entity_visitor)(struct IkarusEntity *, void *); + void * data; + }; + + Data passthru_data{ + blueprint_visitor, + property_visitor, + entity_visitor, + data, + }; + + ikarus_object_visit_const( + object, + [](IkarusBlueprint const * blueprint, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->blueprint_visitor(const_cast(blueprint), passthru_data->data); + }, + [](IkarusProperty const * property, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->property_visitor(const_cast(property), passthru_data->data); + }, + [](IkarusEntity const * entity, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->entity_visitor(const_cast(entity), passthru_data->data); + }, + &passthru_data, + error_out + ); +} + +void ikarus_object_visit_const( + IkarusObject const * object, + void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), + void (*property_visitor)(struct IkarusProperty const *, void *), + void (*entity_visitor)(struct IkarusEntity const *, void *), + void * data, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(object, ); + IKARUS_FAIL_IF_OBJECT_MISSING(object, ); + + switch (ikarus_id_get_object_type(object->id)) { + case IkarusObjectType_Entity: { + auto const * entity = dynamic_cast(object); + IKARUS_FAIL_IF(entity == nullptr, , "object with entity id wasn't a entity", IkarusErrorInfo_LibIkarus_InvalidState); + entity_visitor(entity, data); + } + case IkarusObjectType_Blueprint: { + auto const * blueprint = dynamic_cast(object); + IKARUS_FAIL_IF(blueprint == nullptr, , "object with blueprint id wasn't a blueprint", IkarusErrorInfo_LibIkarus_InvalidState); + blueprint_visitor(blueprint, data); + } + case IkarusObjectType_Property: { + auto const * property = dynamic_cast(object); + IKARUS_FAIL_IF(property == nullptr, , "object with property id wasn't a property", IkarusErrorInfo_LibIkarus_InvalidState); + property_visitor(property, data); + } + default: { + IKARUS_FAIL( + , + fmt::format("unknown object type: {}", ikarus_object_type_to_string(ikarus_id_get_object_type(object->id))), + IkarusErrorInfo_LibIkarus_InvalidState + ); + } + } +} diff --git a/src/objects/object.hpp b/src/objects/object.hpp index c51ddbb..56e141b 100644 --- a/src/objects/object.hpp +++ b/src/objects/object.hpp @@ -1,9 +1,5 @@ #pragma once -#include - -#include - #include struct IkarusObject { diff --git a/src/objects/properties/number_property.cpp b/src/objects/properties/number_property.cpp index d06cfb5..9d0fe13 100644 --- a/src/objects/properties/number_property.cpp +++ b/src/objects/properties/number_property.cpp @@ -1,4 +1,33 @@ #include "number_property.hpp" +#include + +#include + +#include +#include +#include + IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} + +IkarusNumberProperty * ikarus_number_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +) { + return ikarus::util::create_property(project, name, property_source, error_out); +} + +IkarusNumberValue * ikarus_number_property_get_default_value(IkarusNumberProperty * property, IkarusErrorData * error_out) { + return ikarus::util::get_default_value(property, error_out); +} + +void ikarus_number_property_set_default_value( + IkarusNumberProperty * property, + IkarusNumberValue * new_default_value, + IkarusErrorData * error_out +) { + ikarus::util::set_default_value(property, new_default_value, error_out); +} diff --git a/src/objects/properties/number_property.hpp b/src/objects/properties/number_property.hpp index df2ab7e..22ca3d3 100644 --- a/src/objects/properties/number_property.hpp +++ b/src/objects/properties/number_property.hpp @@ -1,8 +1,15 @@ #pragma once -#include +#include + +#include +#include + +struct IkarusNumberProperty : IkarusProperty { +public: + using value_type = IkarusNumberValue; + constexpr auto static PropertyType = IkarusPropertyType_Number; -struct IkarusNumberProperty final : IkarusProperty { public: IkarusNumberProperty(struct IkarusProject * project, IkarusId id); }; diff --git a/src/objects/properties/property.cpp b/src/objects/properties/property.cpp index 498e321..d3c27be 100644 --- a/src/objects/properties/property.cpp +++ b/src/objects/properties/property.cpp @@ -2,8 +2,10 @@ #include +#include #include +#include #include #include #include @@ -11,101 +13,66 @@ IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): IkarusObject{project, id}, - _data{data} {} + data{data} {} -IkarusProperty::Data & IkarusProperty::get_data() { - return _data; -} +IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(property, ); + IKARUS_FAIL_IF_OBJECT_MISSING(property, ); -IkarusProperty::Data const & IkarusProperty::get_data() const { - return _data; -} - -cppbase::Result IkarusProperty::get_property_type(IkarusProject * project, IkarusId id) { - VTRY( - auto const type, - project->db->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", id).on_error([project](auto const & err) { - project->set_error( - fmt::format("failed to fetch unboxed property type: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); - - return cppbase::ok(static_cast(type)); -} - -IKA_API void ikarus_property_delete(IkarusProperty * property) { - TRYRV( + IKARUS_TRYRV_OR_FAIL( , - property->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([property](auto const & err) { - property->project->set_error( - fmt::format("failed to delete property from objects table: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) + "unable to delete property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", property->id) ); property->project->uncache(property); } -IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) { - return IkarusProperty::get_property_type(property->project, property->id).unwrap_value_or(IkarusPropertyType_Toggle); +IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(property, IkarusPropertyType_Toggle); + IKARUS_FAIL_IF_OBJECT_MISSING(property, IkarusPropertyType_Toggle); + + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + IkarusPropertyType_Toggle, + "unable to fetch property type from database: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", property->id) + ); + + return static_cast(ret); } -IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) { - VTRYRV( +IkarusPropertySource const * ikarus_property_get_source(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, - property->project->db - ->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) - .on_error([property](auto const & err) { - property->project->set_error( - fmt::format("failed to fetch property's source: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) + "unable to fetch property source from database: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) ); switch (ikarus_id_get_object_type(source)) { case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->project->get_blueprint(source)}; case IkarusObjectType_Entity: return new IkarusPropertySource{property->project->get_entity(source)}; - default: { - property->project->set_error( - fmt::format("PropertySource is neither blueprint nor entity"), - true, - IkarusErrorInfo_Source_LibIkarus, - IkarusErrorInfo_Type_LibIkarus_InvalidState + default: + IKARUS_FAIL( + nullptr, + fmt::format("invalid property source type: {}", ikarus_object_type_to_string(ikarus_id_get_object_type(source))), + IkarusErrorInfo_LibIkarus_InvalidState ); - - return nullptr; - } } } -IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) { - VTRYRV( - auto const value, - nullptr, - property->project->db - ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id) - .on_error([property](auto const & err) { - property->project->set_error( - fmt::format("failed to fetch property's default value: {}", err), - true, - IkarusErrorInfo_Source_SubSystem, - IkarusErrorInfo_Type_SubSystem_Database - ); - }) - ); +IkarusValue * 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 IkarusValue::from_json(value).unwrap_value_or(nullptr); + return fetch_value_from_db(property->project, error_out, "SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id); } void ikarus_property_visit( @@ -113,15 +80,19 @@ void ikarus_property_visit( void (*toggle_property_visitor)(struct IkarusToggleProperty *, void *), void (*number_property_visitor)(struct IkarusNumberProperty *, void *), void (*text_property_visitor)(struct IkarusTextProperty *, void *), - void * data + void * data, + IkarusErrorData * error_out ) { + 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->get_data() + property->data ); } @@ -130,18 +101,24 @@ void ikarus_property_visit_const( void (*toggle_property_visitor)(struct IkarusToggleProperty const *, void *), void (*number_property_visitor)(struct IkarusNumberProperty const *, void *), void (*text_property_visitor)(struct IkarusTextProperty const *, void *), - void * data + void * data, + IkarusErrorData * error_out ) { + 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->get_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(property); } diff --git a/src/objects/properties/property.hpp b/src/objects/properties/property.hpp index efdb515..3a95d6b 100644 --- a/src/objects/properties/property.hpp +++ b/src/objects/properties/property.hpp @@ -2,23 +2,12 @@ #include -#include - -#include - -#include - #include struct IkarusProperty : IkarusObject { public: using Data = std::variant; -public: - /// \brief Helper to fetch a type for a property that isn't yet wrapped in an object - [[nodiscard]] static cppbase::Result - get_property_type(struct IkarusProject * project, IkarusId id); - public: IkarusProperty(struct IkarusProject * project, IkarusId id, Data data); @@ -31,9 +20,5 @@ public: ~IkarusProperty() override = default; public: - [[nodiscard]] Data & get_data(); - [[nodiscard]] Data const & get_data() const; - -private: - Data _data; + Data data; }; diff --git a/src/objects/properties/property_source.cpp b/src/objects/properties/property_source.cpp index 364ca84..3e31bb3 100644 --- a/src/objects/properties/property_source.cpp +++ b/src/objects/properties/property_source.cpp @@ -3,10 +3,22 @@ #include #include +#include +#include IkarusPropertySource::IkarusPropertySource(Data data): data{data} {} +IkarusId IkarusPropertySource::get_id() const { + return boost::variant2::visit( + cppbase::overloaded { + [](IkarusBlueprint const * blueprint) { return blueprint->id; }, + [](IkarusEntity const * entity) { return entity->id; } + }, + data + ); +} + IkarusPropertySource * ikarus_property_source_create_blueprint(IkarusBlueprint * blueprint) { return new IkarusPropertySource{blueprint}; } diff --git a/src/objects/properties/property_source.hpp b/src/objects/properties/property_source.hpp index a98d664..672021d 100644 --- a/src/objects/properties/property_source.hpp +++ b/src/objects/properties/property_source.hpp @@ -1,7 +1,7 @@ #pragma once #include - +#include #include struct IkarusPropertySource { @@ -19,6 +19,9 @@ public: virtual ~IkarusPropertySource() = default; +public: + [[nodiscard]] IkarusId get_id() const; + public: Data data; }; diff --git a/src/objects/properties/text_property.cpp b/src/objects/properties/text_property.cpp index fd64154..8b3b1a5 100644 --- a/src/objects/properties/text_property.cpp +++ b/src/objects/properties/text_property.cpp @@ -1,4 +1,33 @@ #include "text_property.hpp" +#include + +#include + +#include +#include +#include + IkarusTextProperty::IkarusTextProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} + +IkarusTextProperty * ikarus_text_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +) { + return ikarus::util::create_property(project, name, property_source, error_out); +} + +IkarusTextValue * ikarus_text_property_get_default_value(IkarusTextProperty * property, IkarusErrorData * error_out) { + return ikarus::util::get_default_value(property, error_out); +} + +void ikarus_text_property_set_default_value( + IkarusTextProperty * property, + IkarusTextValue * new_default_value, + IkarusErrorData * error_out +) { + ikarus::util::set_default_value(property, new_default_value, error_out); +} diff --git a/src/objects/properties/text_property.hpp b/src/objects/properties/text_property.hpp index 3a5d163..a3b7e97 100644 --- a/src/objects/properties/text_property.hpp +++ b/src/objects/properties/text_property.hpp @@ -1,8 +1,15 @@ #pragma once -#include +#include + +#include +#include + +struct IkarusTextProperty : IkarusProperty { +public: + using value_type = IkarusTextValue; + constexpr auto static PropertyType = IkarusPropertyType_Text; -struct IkarusTextProperty final : IkarusProperty { public: IkarusTextProperty(struct IkarusProject * project, IkarusId id); }; diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp index db5105b..e0931fa 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/objects/properties/toggle_property.cpp @@ -1,7 +1,33 @@ #include "toggle_property.hpp" +#include + +#include + +#include +#include +#include + IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} -IkarusToggleProperty * -ikarus_toggle_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source) {} +IkarusToggleProperty * ikarus_toggle_property_create( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +) { + return ikarus::util::create_property(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(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(property, new_default_value, error_out); +} diff --git a/src/objects/properties/toggle_property.hpp b/src/objects/properties/toggle_property.hpp index 92bbbc4..247cbcb 100644 --- a/src/objects/properties/toggle_property.hpp +++ b/src/objects/properties/toggle_property.hpp @@ -1,8 +1,15 @@ #pragma once +#include + #include +#include struct IkarusToggleProperty : IkarusProperty { +public: + using value_type = IkarusToggleValue; + constexpr auto static PropertyType = IkarusPropertyType_Toggle; + public: IkarusToggleProperty(struct IkarusProject * project, IkarusId id); }; diff --git a/src/objects/properties/util.hpp b/src/objects/properties/util.hpp new file mode 100644 index 0000000..d44d14d --- /dev/null +++ b/src/objects/properties/util.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +namespace ikarus::util { +template +T * create_property( + struct IkarusProject * project, + char const * name, + struct IkarusPropertySource * property_source, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF_NULL(property_source, nullptr); + IKARUS_FAIL_IF( + cppbase::is_empty_or_blank(name), + nullptr, + fmt::format("{} name cannot be empty or blank", boost::typeindex::type_id().pretty_name()), + IkarusErrorInfo_Client_InvalidInput + ) + + IKARUS_VTRYRV_OR_FAIL( + IkarusId const id, + nullptr, + "failed to create property: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->transact([name, property_source](auto * db) -> cppbase::Result { + TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Property, name)); + auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Property); + TRY(db->execute( + "INSERT INTO `properties`(`id`, `type`, `source`) VALUES(?, ?, ?)", + id, + T::PropertyType, + property_source->get_id() + )); + return cppbase::ok(id); + }) + ); + + auto * ret = dynamic_cast(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().pretty_name()), + IkarusErrorInfo_LibIkarus_InvalidState + ); + + return ret; +} + +template +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(&value->data); + + IKARUS_FAIL_IF( + ret == nullptr, + nullptr, + fmt::format( + "{} default value is not a(n) {}", + boost::typeindex::type_id().pretty_name(), + boost::typeindex::type_id().pretty_name() + ), + IkarusErrorInfo_Database_InvalidState + ); + + return *ret; +} + +template +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 diff --git a/src/persistence/migrations.hpp b/src/persistence/migrations.hpp new file mode 100644 index 0000000..03390d2 --- /dev/null +++ b/src/persistence/migrations.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace ikarus { +CPPBASE_ASSET(m1_initial_layout, "persistence/migrations/m1_initial_layout.sql"); + +class Migration : public sqlitecpp::Migration { +public: + Migration(char const * sql, size_t size): + sql{sql, size} {} + + ~Migration() override = default; + +public: + [[nodiscard]] inline auto get_content() const -> std::string_view override { + return sql; + } + +public: + std::string_view sql; +}; + +#define DECLARE_MIGRATION(name) std::make_unique(static_cast(name()), name##_size()) + +constexpr std::string_view DB_VERSION_KEY = "IKARUS_DB_VERSION"; +std::vector> const MIGRATIONS = []() { + std::vector> ret; + + ret.emplace_back(DECLARE_MIGRATION(m1_initial_layout)); + + return ret; +}(); + +} // namespace ikarus diff --git a/src/persistence/migrations/m0_genesis.sql b/src/persistence/migrations/m0_genesis.sql new file mode 100644 index 0000000..952645d --- /dev/null +++ b/src/persistence/migrations/m0_genesis.sql @@ -0,0 +1,7 @@ +CREATE TABLE `metadata` +( + `key` VARCHAR(255) NOT NULL, + `value` VARCHAR(255) NOT NULL, + + PRIMARY KEY (`key`) +) \ No newline at end of file diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index da56280..7ff2019 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -1,6 +1,12 @@ #include "project.hpp" -#include "ikarus/persistence/project.h" +#include "migrations.hpp" + +#include + +#include + +#include #include #include @@ -9,10 +15,10 @@ #include #include -IkarusProject::IkarusProject(std::string_view name, std::filesystem::path path): +IkarusProject::IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db): name{name}, - path{path}, - db{nullptr}, + path{std::move(path)}, + db{std::move(db)}, _blueprints{}, _properties{}, _entities{} {} @@ -52,3 +58,222 @@ auto IkarusProject::get_property(IkarusId id, IkarusPropertyType type) -> Ikarus 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, nullptr, "unable to check whether path is occupied", 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("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(blueprints_out, ); + + if (blueprints_out_size == 0) { + return; + } + + IkarusId ids[blueprints_out_size]; + + IKARUS_TRYRV_OR_FAIL( + , + "unable to fetch project blueprints from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_many_buffered("SELECT `id` FROM `blueprints`", ids, blueprints_out_size) + ); + + for (size_t i = 0; i < blueprints_out_size; ++i) { + blueprints_out[i] = project->get_blueprint(ids[i]); + } +} + +size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, 0); + + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + 0, + "unable to fetch project blueprint count from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT COUNT(*) FROM `blueprints`") + ); + + return ret; +} + +void ikarus_project_get_entities( + IkarusProject * project, + struct IkarusEntity ** entities_out, + size_t entities_out_size, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(project, ); + IKARUS_FAIL_IF_NULL(entities_out, ); + + if (entities_out_size == 0) { + return; + } + + IkarusId ids[entities_out_size]; + + IKARUS_TRYRV_OR_FAIL( + , + "unable to fetch project entities from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_many_buffered("SELECT `id` FROM `entities`", ids, entities_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 * project, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, 0); + + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + 0, + "unable to fetch project entity count from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT COUNT(*) FROM `entities`") + ); + + return ret; +} diff --git a/src/persistence/project.hpp b/src/persistence/project.hpp index f113f24..f5a00a1 100644 --- a/src/persistence/project.hpp +++ b/src/persistence/project.hpp @@ -1,20 +1,23 @@ #pragma once -#include #include #include #include +#include + #include #include #include #include +namespace fs = boost::filesystem; + /// \private struct IkarusProject { public: - IkarusProject(std::string_view name, std::filesystem::path path); + IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db); public: [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; @@ -23,6 +26,7 @@ public: [[nodiscard]] auto get_entity(IkarusId 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(IkarusId id, IkarusPropertyType type) -> struct IkarusProperty *; auto uncache(struct IkarusProperty * property) -> void; @@ -45,11 +49,13 @@ private: public: std::string name; - std::filesystem::path path; + std::string_view path; std::unique_ptr db; private: - std::unordered_map> _blueprints; - std::unordered_map> _properties; - std::unordered_map> _entities; + std::unordered_map> mutable _blueprints; + std::unordered_map> mutable _properties; + std::unordered_map> mutable _entities; }; + +constexpr std::string_view DB_PROJECT_NAME_KEY = "PROJECT_NAME"; diff --git a/src/values/entity_property_value.cpp b/src/values/entity_property_value.cpp new file mode 100644 index 0000000..d1580ea --- /dev/null +++ b/src/values/entity_property_value.cpp @@ -0,0 +1,21 @@ +#include "values/entity_property_value.hpp" + +#include + +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; +} diff --git a/src/values/entity_property_value.hpp b/src/values/entity_property_value.hpp new file mode 100644 index 0000000..f75d6ba --- /dev/null +++ b/src/values/entity_property_value.hpp @@ -0,0 +1,7 @@ +#pragma once + +struct IkarusEntityPropertyValue { + struct IkarusEntity const * entity; + struct IkarusProperty const * property; + struct IkarusValue const * value; +}; diff --git a/src/values/value.cpp b/src/values/value.cpp index cf9a270..812b2c8 100644 --- a/src/values/value.cpp +++ b/src/values/value.cpp @@ -19,7 +19,7 @@ IkarusValue::IkarusValue(Data data): data(data) {} -cppbase::Result IkarusValue::from_json(boost::json::value const & json) { +cppbase::Result IkarusValue::from_json(boost::json::value json) { if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); } else { diff --git a/src/values/value.hpp b/src/values/value.hpp index adc27ca..94b423d 100644 --- a/src/values/value.hpp +++ b/src/values/value.hpp @@ -5,6 +5,9 @@ #include +#include +#include + struct IkarusValue { public: using Data = boost::variant2::variant; @@ -24,9 +27,50 @@ public: public: struct FromJsonError {}; - [[nodiscard]] static cppbase::Result from_json(boost::json::value const & json); + [[nodiscard]] static cppbase::Result from_json(boost::json::value json); [[nodiscard]] boost::json::value to_json() const; public: Data data; }; + +template<> +struct fmt::formatter { + template + constexpr static auto parse(FormatParseContext & ctx) { + return ctx.end(); + } + + constexpr static auto format([[maybe_unused]]IkarusValue::FromJsonError const & error, fmt::format_context & ctx) { + return fmt::format_to(ctx.out(), "unable to parse ikarus value JSON"); + } +}; + +template +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(query, std::forward(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; +} diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 00a1afc..e837d64 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 00a1afcc5f564f562c436f1ddfa4f44bb6489b17 +Subproject commit e837d64405fb43adefe03994837f5ed5793138c5 From 2a2e28853b77508c9e5a077f721a98da473611dd Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 7 Jan 2024 14:32:44 +0100 Subject: [PATCH 119/166] export compile commands Signed-off-by: Folling --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8389d4f..de1a1a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,8 @@ set(CMAKE_CXX_STANDARD 23) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + add_subdirectory(vendor) add_subdirectory(include) add_subdirectory(src) From b91f59b6cf54532157d59f7bbd89354738efafe5 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 13 Jan 2024 15:54:26 +0100 Subject: [PATCH 120/166] rename libikarus => ikarus in cmake & update sqlitecpp Signed-off-by: Folling --- CMakeLists.txt | 18 +++++++++--------- vendor/sqlitecpp | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de1a1a9..c5abd8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,30 +19,30 @@ add_subdirectory(src) find_package(Boost COMPONENTS system filesystem REQUIRED) add_library( - libikarus SHARED + ikarus SHARED ${INCLUDE_FILES} ${SOURCE_FILES} ) target_include_directories( - libikarus PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/include + ikarus PUBLIC + $ ) target_include_directories( - libikarus PRIVATE + ikarus PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src ) target_link_libraries( - libikarus PRIVATE + ikarus PRIVATE cppbase sqlitecpp ${Boost_LIBRARIES} ) target_include_directories( - libikarus PRIVATE + ikarus PRIVATE ${Boost_INCLUDE_DIRS} ) @@ -50,9 +50,9 @@ if (LIBIKARUS_ENABLE_LINTS) find_program(IWYU_PATH NAMES include-what-you-use iwyu REQUIRED) find_program(CLANG_TIDY_PATH NAMES clang-tidy REQUIRED) - set_property(TARGET libikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) + set_property(TARGET ikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) set_property( - TARGET libikarus + TARGET ikarus PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY_PATH}; ) endif () @@ -83,7 +83,7 @@ if (LIBIKARUS_BUILD_DOCS) ) add_dependencies( - libikarus + ikarus libikarus_docs ) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index e837d64..e41d51e 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit e837d64405fb43adefe03994837f5ed5793138c5 +Subproject commit e41d51ed750c5f0a37fc1894c5dd382df32ead26 From c435384f95eba06bec9ba42662e6e5c19e7e7e51 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 13 Jan 2024 16:04:03 +0100 Subject: [PATCH 121/166] fix missing include Signed-off-by: Folling --- CMakeLists.txt | 2 +- include/ikarus/persistence/project.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5abd8f..01e5f44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ add_library( target_include_directories( ikarus PUBLIC - $ + ${CMAKE_CURRENT_LIST_DIR}/include ) target_include_directories( diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index c4406f8..f08107b 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -3,6 +3,7 @@ /// \file project.h /// \author Folling +#include #include #include From 1b36e56cf5c98c83420b97f554db05269e11eac4 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 13 Jan 2024 23:46:33 +0100 Subject: [PATCH 122/166] fix various issues Signed-off-by: Folling --- include/ikarus/persistence/project.h | 2 +- include/ikarus/stdtypes.h | 2 +- src/global.cpp | 7 +++ src/id.cpp | 2 - src/objects/blueprint.cpp | 2 +- src/objects/entity.cpp | 2 +- src/objects/properties/util.hpp | 2 +- src/persistence/migrations.hpp | 2 +- src/persistence/migrations/m0_genesis.sql | 7 --- ...itial_layout.sql => m0_initial_layout.sql} | 45 +++++++------------ src/persistence/project.cpp | 8 ++-- vendor/sqlitecpp | 2 +- 12 files changed, 33 insertions(+), 50 deletions(-) create mode 100644 src/global.cpp delete mode 100644 src/persistence/migrations/m0_genesis.sql rename src/persistence/migrations/{m1_initial_layout.sql => m0_initial_layout.sql} (75%) diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index f08107b..117909e 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -101,7 +101,7 @@ IKA_API void ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Gets the entities of a project. /// \param project The project to get the entities of. diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h index 6fa7e95..4274e3f 100644 --- a/include/ikarus/stdtypes.h +++ b/include/ikarus/stdtypes.h @@ -3,8 +3,8 @@ #ifdef __cplusplus #include #include -// NOLINTNEXTLINE(google-global-names-in-headers) using std::size_t; #else +#include #include #endif diff --git a/src/global.cpp b/src/global.cpp new file mode 100644 index 0000000..8b1929a --- /dev/null +++ b/src/global.cpp @@ -0,0 +1,7 @@ +#include "ikarus/global.h" + +#include + +void ikarus_free(void * ptr) { + std::free(ptr); +} \ No newline at end of file diff --git a/src/id.cpp b/src/id.cpp index 463aa3d..031c490 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -1,7 +1,5 @@ #include "ikarus/id.h" -#include - #include constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; diff --git a/src/objects/blueprint.cpp b/src/objects/blueprint.cpp index 971bd13..e5f3f0c 100644 --- a/src/objects/blueprint.cpp +++ b/src/objects/blueprint.cpp @@ -26,7 +26,7 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c "failed to create blueprint: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Blueprint, name)); + TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Blueprint, name, "")); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id)); return cppbase::ok(id); diff --git a/src/objects/entity.cpp b/src/objects/entity.cpp index 174123e..6c1839a 100644 --- a/src/objects/entity.cpp +++ b/src/objects/entity.cpp @@ -21,7 +21,7 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * "failed to create entity: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Entity, name)); + TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Entity, name, "")); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Entity); TRY(db->execute("INSERT INTO `entities`(`id`) VALUES(?)", id)); return cppbase::ok(id); diff --git a/src/objects/properties/util.hpp b/src/objects/properties/util.hpp index d44d14d..2e11475 100644 --- a/src/objects/properties/util.hpp +++ b/src/objects/properties/util.hpp @@ -36,7 +36,7 @@ T * create_property( "failed to create property: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name, property_source](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Property, name)); + TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Property, name, "")); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Property); TRY(db->execute( "INSERT INTO `properties`(`id`, `type`, `source`) VALUES(?, ?, ?)", diff --git a/src/persistence/migrations.hpp b/src/persistence/migrations.hpp index 03390d2..3416ffb 100644 --- a/src/persistence/migrations.hpp +++ b/src/persistence/migrations.hpp @@ -8,7 +8,7 @@ #include namespace ikarus { -CPPBASE_ASSET(m1_initial_layout, "persistence/migrations/m1_initial_layout.sql"); +CPPBASE_ASSET(m0_initial_layout, "persistence/migrations/m0_initial_layout.sql"); class Migration : public sqlitecpp::Migration { public: diff --git a/src/persistence/migrations/m0_genesis.sql b/src/persistence/migrations/m0_genesis.sql deleted file mode 100644 index 952645d..0000000 --- a/src/persistence/migrations/m0_genesis.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE `metadata` -( - `key` VARCHAR(255) NOT NULL, - `value` VARCHAR(255) NOT NULL, - - PRIMARY KEY (`key`) -) \ No newline at end of file diff --git a/src/persistence/migrations/m1_initial_layout.sql b/src/persistence/migrations/m0_initial_layout.sql similarity index 75% rename from src/persistence/migrations/m1_initial_layout.sql rename to src/persistence/migrations/m0_initial_layout.sql index ea24c56..99ba61b 100644 --- a/src/persistence/migrations/m1_initial_layout.sql +++ b/src/persistence/migrations/m0_initial_layout.sql @@ -1,27 +1,22 @@ CREATE TABLE `objects` ( `do_not_access_rowid_alias` INTEGER PRIMARY KEY, - `object_type` INT NOT NULL, - `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`object_type` << 56) -) VIRTUAL, + `type` INT NOT NULL, + `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`type` << 56)) VIRTUAL, `name` TEXT NOT NULL, `information` TEXT NOT NULL ) STRICT; CREATE UNIQUE INDEX `object_id` ON `objects` (`id`); -CREATE INDEX `object_type` ON `objects` (`object_type`); +CREATE INDEX `object_type` ON `objects` (`type`); -CREATE -VIRTUAL TABLE `objects_fts` USING fts5 +CREATE VIRTUAL TABLE `objects_fts` USING fts5 ( `name`, `information`, - content= - 'objects', - content_rowid= - 'id', - tokenize= - "unicode61 remove_diacritics 2 tokenchars '-_'" + content='objects', + content_rowid='id', + tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" ); CREATE TABLE `entities` @@ -73,24 +68,18 @@ CREATE VIRTUAL TABLE `property_default_value_fts` USING fts5 ( `default_value`, - content= - 'properties', - content_rowid= - 'object_id', - tokenize= - "unicode61 remove_diacritics 2 tokenchars '-_'" + content='properties', + content_rowid='object_id', + tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" ); CREATE VIRTUAL TABLE `property_settings_fts` USING fts5 ( `settings`, - content= - 'properties', - content_rowid= - 'object_id', - tokenize= - "unicode61 remove_diacritics 2 tokenchars '-_'" + content='properties', + content_rowid='object_id', + tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" ); CREATE TABLE `values` @@ -108,8 +97,6 @@ CREATE VIRTUAL TABLE `values_fts` USING fts5 ( `value`, - content= - 'values', - tokenize= - "unicode61 remove_diacritics 2 tokenchars '-_'" -) + content='values', + tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" +); diff --git a/src/persistence/project.cpp b/src/persistence/project.cpp index 7ff2019..41ddbf7 100644 --- a/src/persistence/project.cpp +++ b/src/persistence/project.cpp @@ -1,19 +1,17 @@ -#include "project.hpp" - -#include "migrations.hpp" +#include "ikarus/persistence/project.h" #include #include -#include - #include #include #include #include #include #include +#include +#include IkarusProject::IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db): name{name}, diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index e41d51e..66f5747 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit e41d51ed750c5f0a37fc1894c5dd382df32ead26 +Subproject commit 66f5747f1ad3e43960e9a77755e770229b90004e From fafb16dd05626b7b88a6b1121ccdf53f0cc74bc7 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 13 Jan 2024 23:56:30 +0100 Subject: [PATCH 123/166] update sqlitecpp Signed-off-by: Folling --- src/persistence/migrations.hpp | 2 +- vendor/sqlitecpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/persistence/migrations.hpp b/src/persistence/migrations.hpp index 3416ffb..c777e84 100644 --- a/src/persistence/migrations.hpp +++ b/src/persistence/migrations.hpp @@ -32,7 +32,7 @@ constexpr std::string_view DB_VERSION_KEY = "IKARUS_DB_VERSION"; std::vector> const MIGRATIONS = []() { std::vector> ret; - ret.emplace_back(DECLARE_MIGRATION(m1_initial_layout)); + ret.emplace_back(DECLARE_MIGRATION(m0_initial_layout)); return ret; }(); diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 66f5747..f27ef69 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 66f5747f1ad3e43960e9a77755e770229b90004e +Subproject commit f27ef69131166549e9c250b658bf26136b09208b From ca411eabaff34eb56849bb3c250429d35271fed4 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 14 Jan 2024 00:12:52 +0100 Subject: [PATCH 124/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index f27ef69..eadf323 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit f27ef69131166549e9c250b658bf26136b09208b +Subproject commit eadf3237bfa853763b332777e9dc0f16df8cca71 From 68db1d3542382a3e81d6e6848d2a7c8b5ce555fb Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 14 Jan 2024 02:11:13 +0100 Subject: [PATCH 125/166] add object_get/set_name/information Signed-off-by: Folling --- include/ikarus/objects/object.h | 38 ++++++++++++++++++++ src/objects/object.cpp | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index 9dd3b39..7dcc97f 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -22,17 +22,55 @@ struct IkarusObject; /// \brief Fetches the project of an object. /// \param object The object to fetch the project from. /// \pre \li Must not be null. +/// \pre \li Must exist. /// \param error_out \see errors.h /// \return The project of the object or null if an error occurs. IKA_API struct IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErrorData * error_out); +/// \brief Fetches the name of an object. +/// \param object The object to fetch the name from. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The name of the object or null if an error occurs. +IKA_API char const * ikarus_object_get_name(IkarusObject const * object, IkarusErrorData * error_out); + +/// \brief Sets the name of an object. +/// \param object The object to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_name The new name of the object. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param error_out \see errors.h +IKA_API void ikarus_object_set_name(IkarusObject const * object, char const * new_name, IkarusErrorData * error_out); + +/// \brief Fetches the information of an object. +/// \param object The object to fetch the information from. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The information of the object or null if an error occurs. +IKA_API char const * ikarus_object_get_info(IkarusObject const * object, IkarusErrorData * error_out); + +/// \brief Sets the information of an object. +/// \param object The object to set the information of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param new_info The new information of the object. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +IKA_API void ikarus_object_set_info(IkarusObject const * object, char const * new_info, IkarusErrorData * error_out); + /// \brief Compares two objects for equality. /// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the /// same object. /// \param lhs The left hand side object. /// \pre \li Must not be null. +/// \pre \li Must exist. /// \param rhs The right hand side object. /// \pre \li Must not be null. +/// \pre \li Must exist. /// \param error_out \see errors.h /// \return True if the objects are equal, false otherwise. IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out); diff --git a/src/objects/object.cpp b/src/objects/object.cpp index 2f52b48..e8a3353 100644 --- a/src/objects/object.cpp +++ b/src/objects/object.cpp @@ -2,6 +2,9 @@ #include +#include + +#include #include #include @@ -21,6 +24,64 @@ IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErr return object->project; } +char const * ikarus_object_get_name(IkarusObject const * object, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(object, nullptr); + IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + std::string ret, + nullptr, + "unable to get object name: {}", + IkarusErrorInfo_Database_QueryFailed, + object->project->db->template query_one("SELECT `name` FROM `objects` WHERE `id` = ?", object->id) + ); + + return strdup(ret.data()); +} + +void ikarus_object_set_name(IkarusObject * 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(cppbase::is_empty_or_blank(name), , "name must not be empty", IkarusErrorInfo_Client_InvalidInput); + + IKARUS_TRYRV_OR_FAIL( + , + "unable to set object name: {}", + IkarusErrorInfo_Database_QueryFailed, + object->project->db->template execute("UPDATE `objects` SET `name` = ? WHERE `id` = ?", name, object->id) + ); +} + +char const * ikarus_object_get_information(IkarusObject const * object, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(object, nullptr); + IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + std::string ret, + nullptr, + "unable to get object information: {}", + IkarusErrorInfo_Database_QueryFailed, + object->project->db->template query_one("SELECT `information` FROM `objects` WHERE `id` = ?", object->id) + ); + + return strdup(ret.data()); +} + +void ikarus_object_set_information(IkarusObject * object, char const * information, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(object, ); + IKARUS_FAIL_IF_OBJECT_MISSING(object, ); + IKARUS_FAIL_IF_NULL(information, ); + IKARUS_FAIL_IF(cppbase::is_empty_or_blank(information), , "information must not be empty", IkarusErrorInfo_Client_InvalidInput); + + IKARUS_TRYRV_OR_FAIL( + , + "unable to set object information: {}", + IkarusErrorInfo_Database_QueryFailed, + object->project->db->template execute("UPDATE `objects` SET `information` = ? WHERE `id` = ?", information, object->id) + ); +} + bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(lhs, false); IKARUS_FAIL_IF_OBJECT_MISSING(lhs, false); From 8f82b0e3da28486822035774ccef853b71a80bdc Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 14 Jan 2024 13:46:07 +0100 Subject: [PATCH 126/166] fixup id generation Signed-off-by: Folling --- src/id.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/id.cpp b/src/id.cpp index 031c490..4269215 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -3,7 +3,7 @@ #include constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; -constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; +constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; auto ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); From 70fca82425aabfeb60566737e1c05ec0cb255abf Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 15 Jan 2024 03:37:26 +0100 Subject: [PATCH 127/166] add default values as param to property_create funcs Signed-off-by: Folling --- include/ikarus/objects/properties/number_property.h | 3 +++ include/ikarus/objects/properties/text_property.h | 3 +++ include/ikarus/objects/properties/toggle_property.h | 3 +++ src/objects/properties/number_property.cpp | 6 ++++-- src/objects/properties/text_property.cpp | 3 ++- src/objects/properties/toggle_property.cpp | 3 ++- src/objects/properties/util.hpp | 9 ++++++--- vendor/sqlitecpp | 2 +- 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 8ca4ffd..2fcf63b 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -24,12 +24,15 @@ struct IkarusNumberProperty; /// \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 ); diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index 0408614..5e59d92 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -24,12 +24,15 @@ struct IkarusTextProperty; /// \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 ); diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index 892d418..403f392 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -24,12 +24,15 @@ struct IkarusToggleProperty; /// \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 IkarusToggleProperty * ikarus_toggle_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, + struct IkarusToggleValue * default_value, IkarusErrorData * error_out ); diff --git a/src/objects/properties/number_property.cpp b/src/objects/properties/number_property.cpp index 9d0fe13..b94429d 100644 --- a/src/objects/properties/number_property.cpp +++ b/src/objects/properties/number_property.cpp @@ -1,9 +1,10 @@ -#include "number_property.hpp" +#include "ikarus/objects/properties/number_property.h" #include #include +#include #include #include #include @@ -15,9 +16,10 @@ IkarusNumberProperty * ikarus_number_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, + struct IkarusNumberValue * default_value, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, error_out); + return ikarus::util::create_property(project, name, property_source, default_value, error_out); } IkarusNumberValue * ikarus_number_property_get_default_value(IkarusNumberProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/text_property.cpp b/src/objects/properties/text_property.cpp index 8b3b1a5..c7541ff 100644 --- a/src/objects/properties/text_property.cpp +++ b/src/objects/properties/text_property.cpp @@ -15,9 +15,10 @@ IkarusTextProperty * ikarus_text_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, + struct IkarusTextValue * default_value, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, error_out); + return ikarus::util::create_property(project, name, property_source, default_value, error_out); } IkarusTextValue * ikarus_text_property_get_default_value(IkarusTextProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/toggle_property.cpp b/src/objects/properties/toggle_property.cpp index e0931fa..2cde654 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/objects/properties/toggle_property.cpp @@ -15,9 +15,10 @@ IkarusToggleProperty * ikarus_toggle_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, + struct IkarusToggleValue * default_value, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, error_out); + return ikarus::util::create_property(project, name, property_source, default_value, error_out); } IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/util.hpp b/src/objects/properties/util.hpp index 2e11475..d9321c7 100644 --- a/src/objects/properties/util.hpp +++ b/src/objects/properties/util.hpp @@ -18,6 +18,7 @@ T * create_property( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, + typename T::value_type * default_value, IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(project, nullptr); @@ -29,20 +30,22 @@ T * create_property( fmt::format("{} name cannot be empty or blank", boost::typeindex::type_id().pretty_name()), IkarusErrorInfo_Client_InvalidInput ) + IKARUS_FAIL_IF_NULL(default_value, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, nullptr, "failed to create property: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->transact([name, property_source](auto * db) -> cppbase::Result { + project->db->transact([name, property_source, default_value](auto * db) -> cppbase::Result { TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Property, name, "")); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Property); TRY(db->execute( - "INSERT INTO `properties`(`id`, `type`, `source`) VALUES(?, ?, ?)", + "INSERT INTO `properties`(`id`, `type`, `source`, `default_value`) VALUES(?, ?, ?, ?)", id, T::PropertyType, - property_source->get_id() + property_source->get_id(), + boost::json::serialize(default_value->to_json()) )); return cppbase::ok(id); }) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index eadf323..77e7260 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit eadf3237bfa853763b332777e9dc0f16df8cca71 +Subproject commit 77e726036b52fff6f82ff2d44081f05aee7408a8 From dc8b7712b0dc1994c8e41b4c2e53b3f8f27fcc83 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 30 Jan 2024 09:31:08 +0100 Subject: [PATCH 128/166] add src/ikarus subdir and make names unique for objects per scope Signed-off-by: Folling --- .clang-tidy | 26 ++- clang-format.txt | 192 ------------------ include/ikarus/errors.h | 3 +- include/ikarus/id.h | 8 +- include/ikarus/objects/blueprint.h | 41 ++-- include/ikarus/objects/entity.h | 41 ++-- include/ikarus/objects/object.h | 68 +------ include/ikarus/objects/properties/property.h | 3 +- .../{property_source.h => property_scope.h} | 10 +- include/ikarus/persistence/project.h | 88 +++++--- src/{ => ikarus}/errors.cpp | 4 +- src/{ => ikarus}/errors.hpp | 0 src/{ => ikarus}/global.cpp | 0 src/{ => ikarus}/id.cpp | 2 +- src/{ => ikarus}/objects/blueprint.cpp | 43 ++-- src/{ => ikarus}/objects/blueprint.hpp | 8 +- src/{ => ikarus}/objects/entity.cpp | 47 ++--- src/{ => ikarus}/objects/entity.hpp | 8 +- src/ikarus/objects/object.cpp | 93 +++++++++ src/{ => ikarus}/objects/object.hpp | 5 + src/{ => ikarus}/objects/object_type.cpp | 0 .../objects/properties/number_property.cpp | 15 +- .../objects/properties/number_property.hpp | 5 +- .../objects/properties/property.cpp | 29 ++- .../objects/properties/property.hpp | 7 +- .../objects/properties/property_scope.cpp} | 39 ++-- .../objects/properties/property_scope.hpp | 39 ++++ .../objects/properties/text_property.cpp | 12 +- .../objects/properties/text_property.hpp | 5 +- .../objects/properties/toggle_property.cpp | 12 +- .../objects/properties/toggle_property.hpp | 5 +- src/{ => ikarus}/objects/properties/util.hpp | 34 ++-- src/ikarus/objects/util.hpp | 125 ++++++++++++ src/{ => ikarus}/persistence/migrations.hpp | 2 +- .../migrations/m0_initial_layout.sql | 54 +---- src/{ => ikarus}/persistence/project.cpp | 16 +- src/{ => ikarus}/persistence/project.hpp | 0 .../values/entity_property_value.cpp | 4 +- .../values/entity_property_value.hpp | 0 src/{ => ikarus}/values/number_value.cpp | 4 +- src/{ => ikarus}/values/number_value.hpp | 2 +- src/{ => ikarus}/values/text_value.cpp | 4 +- src/{ => ikarus}/values/text_value.hpp | 2 +- src/{ => ikarus}/values/toggle_value.cpp | 4 +- src/{ => ikarus}/values/toggle_value.hpp | 2 +- src/{ => ikarus}/values/value.cpp | 9 +- src/{ => ikarus}/values/value.hpp | 6 +- src/{ => ikarus}/values/value_base.hpp | 0 src/objects/object.cpp | 170 ---------------- src/objects/properties/property_source.hpp | 27 --- vendor/sqlitecpp | 2 +- 51 files changed, 590 insertions(+), 735 deletions(-) delete mode 100644 clang-format.txt rename include/ikarus/objects/properties/{property_source.h => property_scope.h} (86%) rename src/{ => ikarus}/errors.cpp (98%) rename src/{ => ikarus}/errors.hpp (100%) rename src/{ => ikarus}/global.cpp (100%) rename src/{ => ikarus}/id.cpp (93%) rename src/{ => ikarus}/objects/blueprint.cpp (83%) rename src/{ => ikarus}/objects/blueprint.hpp (71%) rename src/{ => ikarus}/objects/entity.cpp (88%) rename src/{ => ikarus}/objects/entity.hpp (72%) create mode 100644 src/ikarus/objects/object.cpp rename src/{ => ikarus}/objects/object.hpp (82%) rename src/{ => ikarus}/objects/object_type.cpp (100%) rename src/{ => ikarus}/objects/properties/number_property.cpp (70%) rename src/{ => ikarus}/objects/properties/number_property.hpp (77%) rename src/{ => ikarus}/objects/properties/property.cpp (81%) rename src/{ => ikarus}/objects/properties/property.hpp (79%) rename src/{objects/properties/property_source.cpp => ikarus/objects/properties/property_scope.cpp} (58%) create mode 100644 src/ikarus/objects/properties/property_scope.hpp rename src/{ => ikarus}/objects/properties/text_property.cpp (77%) rename src/{ => ikarus}/objects/properties/text_property.hpp (77%) rename src/{ => ikarus}/objects/properties/toggle_property.cpp (77%) rename src/{ => ikarus}/objects/properties/toggle_property.hpp (77%) rename src/{ => ikarus}/objects/properties/util.hpp (73%) create mode 100644 src/ikarus/objects/util.hpp rename src/{ => ikarus}/persistence/migrations.hpp (90%) rename src/{ => ikarus}/persistence/migrations/m0_initial_layout.sql (61%) rename src/{ => ikarus}/persistence/project.cpp (95%) rename src/{ => ikarus}/persistence/project.hpp (100%) rename src/{ => ikarus}/values/entity_property_value.cpp (90%) rename src/{ => ikarus}/values/entity_property_value.hpp (100%) rename src/{ => ikarus}/values/number_value.cpp (96%) rename src/{ => ikarus}/values/number_value.hpp (94%) rename src/{ => ikarus}/values/text_value.cpp (96%) rename src/{ => ikarus}/values/text_value.hpp (94%) rename src/{ => ikarus}/values/toggle_value.cpp (96%) rename src/{ => ikarus}/values/toggle_value.hpp (94%) rename src/{ => ikarus}/values/value.cpp (96%) rename src/{ => ikarus}/values/value.hpp (91%) rename src/{ => ikarus}/values/value_base.hpp (100%) delete mode 100644 src/objects/object.cpp delete mode 100644 src/objects/properties/property_source.hpp diff --git a/.clang-tidy b/.clang-tidy index 63731e9..49b97c2 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,26 +1,24 @@ Checks: >- -*, - bugprone-*, -bugprone-lambda-function-name, - cppcoreguidelines-*, -cppcoreguidelines-macro-usage, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes, - clang-analyzer-*, - google-*, -google-readability-todo, - modernize-*, -modernize-use-trailing-return-type, - performance-*, -performance-enum-size, - portability-*, - readability-*, -readability-redundant-access-specifiers +# bugprone-*, -bugprone-lambda-function-name, +# cppcoreguidelines-*, -cppcoreguidelines-macro-usage, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes, +# clang-analyzer-*, +# google-*, -google-readability-todo, +# modernize-*, -modernize-use-trailing-return-type, +# performance-*, -performance-enum-size, +# portability-*, +# readability-*, -readability-redundant-access-specifiers CheckOptions: readability-identifier-length.IgnoredParameterNames: '^(db|rc|id|ec)$' readability-identifier-length.IgnoredLoopCounterNames: '^[ij]$' readability-identifier-length.IgnoredVariableNames: '^(db|rc|id|ec)$' cppcoreguidelines-avoid-do-while.IgnoreMacros: Yes HeaderFileExtensions: - - h - - hpp - - tpp - - ipp +# - hpp +# - tpp +# - ipp ImplementationFileExtensions: - - c - - cpp +# - cpp FormatStyle: file InheritParentConfig: false WarningsAsErrors: '*' diff --git a/clang-format.txt b/clang-format.txt deleted file mode 100644 index a600edb..0000000 --- a/clang-format.txt +++ /dev/null @@ -1,192 +0,0 @@ -BasedOnStyle: Google - -AccessModifierOffset: -4 - -AlignAfterOpenBracket: BlockIndent -AlignArrayOfStructures: Right -AlignConsecutiveAssignments: - Enabled: false -AlignConsecutiveBitFields: - Enabled: false -AlignConsecutiveDeclarations: - Enabled: false -AlignConsecutiveMacros: AcrossEmptyLines -AlignEscapedNewlines: Left -AlignOperands: Align -AlignTrailingComments: true - -AllowAllArgumentsOnNextLine: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: Empty -AllowShortCaseLabelsOnASingleLine: true -AllowShortEnumsOnASingleLine: true -AllowShortFunctionsOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortLambdasOnASingleLine: All -AllowShortLoopsOnASingleLine: true - -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: Yes - -BinPackArguments: false -BinPackParameters: false - -BitFieldColonSpacing: Both - -BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyNamespace: false - SplitEmptyRecord: false - -BracedInitializerIndentWidth: 4 - -# BreakAdjacentStringLiterals: true -BreakAfterAttributes: Never -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Custom -BreakBeforeConceptDeclarations: Always -BreakBeforeInlineASMColon: OnlyMultiline -BreakBeforeTernaryOperators: false -BreakConstructorInitializers: AfterColon -BreakInheritanceList: AfterColon -BreakStringLiterals: false - -ColumnLimit: 140 - -CompactNamespaces: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 - -Cpp11BracedListStyle: true - -DerivePointerAlignment: false - -EmptyLineAfterAccessModifier: Never -EmptyLineBeforeAccessModifier: Always - -ExperimentalAutoDetectBinPacking: true - -FixNamespaceComments: true - -IncludeBlocks: Regroup -IncludeCategories: - - Regex: '^".+\.(h|hpp)"$' - Priority: 1 - - Regex: '^<[a-z0-9_]+\.h>$' - Priority: 2 - - Regex: '^<[a-z0-9_]+>$' - Priority: 3 - - Regex: '^$' - Priority: 4 - - Regex: '^$' - Priority: 5 - - Regex: '^$' - Priority: 6 - - Regex: '^$' - Priority: 7 - - Regex: '^$' - Priority: 8 - - Regex: '^$' - Priority: 9 - - Regex: '^$' - Priority: 10 - - Regex: '^$' - Priority: 11 - - Regex: '^$' - Priority: 12 - - Regex: '^$' - Priority: 13 - -IndentAccessModifiers: false -IndentCaseBlocks: false -IndentCaseLabels: false -IndentExternBlock: NoIndent -IndentGotoLabels: false -IndentPPDirectives: None -IndentRequiresClause: true -IndentWidth: 4 -IndentWrappedFunctionNames: false -InsertBraces: true -InsertNewlineAtEOF: true -InsertTrailingCommas: Wrapped - -IntegerLiteralSeparator: - Binary: -1 - Decimal: 3 - Hex: -1 - -KeepEmptyLinesAtEOF: false -KeepEmptyLinesAtTheStartOfBlocks: false - -LambdaBodyIndentation: Signature -Language: Cpp - -LineEnding: LF - -MaxEmptyLinesToKeep: 1 - -NamespaceIndentation: None - -PPIndentWidth: -1 -PackConstructorInitializers: Never - -PointerAlignment: Middle -QualifierAlignment: Right -# QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ] -ReferenceAlignment: Middle - -ReflowComments: true -RemoveBracesLLVM: false -RemoveParentheses: MultipleParentheses -RemoveSemicolon: true - -RequiresClausePosition: OwnLine -RequiresExpressionIndentation: OuterScope - -SeparateDefinitionBlocks: Always - -SortIncludes: CaseInsensitive -SortUsingDeclarations: LexicographicNumeric - -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: false -SpaceAroundPointerQualifiers: Both -SpaceBeforeAssignmentOperators: true -SpaceBeforeCaseColon: false -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: false -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: true -SpaceBeforeSquareBrackets: false -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesInAngles: false -SpacesInCStyleCastParentheses: false -SpacesInConditionalStatement: false -SpacesInContainerLiterals: false -SpacesInLineCommentPrefix: - Minimum: 1 - Maximum: 1 -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: c++20 - -TabWidth: 4 -UseTab: Never diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index a4887a3..ce11788 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -1,6 +1,6 @@ #pragma once -/// \file global.h +/// \file errors.h /// \author Folling #include @@ -92,6 +92,7 @@ enum IkarusErrorInfo { IkarusErrorInfo_LibIkarus_Timeout = 0x06000003, }; +/// \brief The maximum length of an error message. size_t const IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT = 128; /// \brief The data stored for an error diff --git a/include/ikarus/id.h b/include/ikarus/id.h index ae545a9..c76e341 100644 --- a/include/ikarus/id.h +++ b/include/ikarus/id.h @@ -11,8 +11,6 @@ #include #include -IKARUS_BEGIN_HEADER - /// \defgroup id Ids /// \brief Ids are used to identify objects in the database. /// \details They are stored as 64 bit integers with the following layout: @@ -22,6 +20,8 @@ IKARUS_BEGIN_HEADER /// - last 56 bits: incremented counter generated by the database /// @{ +IKARUS_BEGIN_HEADER + /// \brief A wrapper around a 64 bit integer that represents the id of an object. /// \details They are stored as 64 bit integers with the following layout: /// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. @@ -41,6 +41,6 @@ IKA_API IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType typ /// \return The object type of the given id. IKA_API IkarusObjectType ikarus_id_get_object_type(IkarusId id); -/// @} - IKARUS_END_HEADER + +/// @} diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 3ce07ce..adfacd4 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -25,9 +25,9 @@ struct IkarusBlueprint; /// \param name The name of the blueprint. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \pre \li Must be unique among all blueprints in the project. /// \param error_out \see errors.h /// \return The created blueprint or null if an error occurs. -/// \remark Must be freed using #ikarus_free. IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Deletes & frees a blueprint. @@ -38,6 +38,33 @@ IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project /// \remark The blueprint must not be accessed after deletion. IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * error_out); +/// \brief Gets the project a blueprint is part of. +/// \param blueprint The blueprint 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 blueprint is part of or null if an error occurs. +IKA_API IkarusProject * ikarus_blueprint_get_project(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); + +/// \brief Gets the name of a blueprint. +/// \param blueprint The blueprint 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 blueprint or null if an error occurs. +IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); + +/// \brief Sets the name of a blueprint. +/// \param blueprint The blueprint to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The new name of the blueprint. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \pre \li Must be unique among all blueprints in the project. +/// \param error_out \see errors.h +IKA_API void ikarus_blueprint_set_name(IkarusBlueprint * blueprint, char const * name, IkarusErrorData * error_out); + /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. /// \pre \li Must not be null. @@ -86,18 +113,6 @@ IKA_API void ikarus_blueprint_get_linked_entities( /// \return The number of linked entities or undefined if an error occurs. IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); -/// \brief Casts a blueprint to an object. -/// \param blueprint The blueprint to cast. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The blueprint represented as an object or null if an error occurs. -/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint, IkarusErrorData * error_out); - -/// \see ikarus_blueprint_to_object -IKA_API struct IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); - IKARUS_END_HEADER // @} diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 9610230..53abc70 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -41,9 +41,9 @@ struct IkarusEntity; /// \param name The name of the entity. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \pre \li Must be unique among all entities in the project. /// \param error_out \see errors.h /// \return The created entity or null if an error occurs. -/// \remark Must be freed using #ikarus_free. IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Deletes an entity. @@ -54,6 +54,33 @@ IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char /// \remark The entity must not be accessed after deletion. IKA_API void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); +/// \brief Gets the project an entity is part of. +/// \param entity The entity 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 entity is part of or null if an error occurs. +IKA_API IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity, IkarusErrorData * error_out); + +/// \brief Gets the name of an entity. +/// \param entity The entity 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 entity or null if an error occurs. +IKA_API char const * ikarus_entity_get_name(IkarusEntity const * entity, IkarusErrorData * error_out); + +/// \brief Sets the name of an entity. +/// \param entity The entity to set the name of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The new name of the entity. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \pre \li Must be unique among all entities in the project. +/// \param error_out \see errors.h +IKA_API void ikarus_entity_set_name(IkarusEntity * entity, char const * name, IkarusErrorData * error_out); + /// \brief Checks if an entity is linked to a blueprint. /// \param entity The entity to check. /// \pre \li Must not be null. @@ -182,18 +209,6 @@ IKA_API void ikarus_entity_set_value( IkarusErrorData * error_out ); -/// \brief Casts an entity to an object. -/// \param entity The entity to cast. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The entity represented as an object or null if an error occurs. -/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity, IkarusErrorData * error_out); - -/// \see ikarus_entity_to_object -IKA_API struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity, IkarusErrorData * error_out); - IKARUS_END_HEADER // @} diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h index 7dcc97f..6a46e17 100644 --- a/include/ikarus/objects/object.h +++ b/include/ikarus/objects/object.h @@ -19,62 +19,6 @@ IKARUS_BEGIN_HEADER /// \brief A generic object. Wraps all types of objects, including folders. struct IkarusObject; -/// \brief Fetches the project of an object. -/// \param object The object to fetch the project from. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The project of the object or null if an error occurs. -IKA_API struct IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErrorData * error_out); - -/// \brief Fetches the name of an object. -/// \param object The object to fetch the name from. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The name of the object or null if an error occurs. -IKA_API char const * ikarus_object_get_name(IkarusObject const * object, IkarusErrorData * error_out); - -/// \brief Sets the name of an object. -/// \param object The object to set the name of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_name The new name of the object. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param error_out \see errors.h -IKA_API void ikarus_object_set_name(IkarusObject const * object, char const * new_name, IkarusErrorData * error_out); - -/// \brief Fetches the information of an object. -/// \param object The object to fetch the information from. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The information of the object or null if an error occurs. -IKA_API char const * ikarus_object_get_info(IkarusObject const * object, IkarusErrorData * error_out); - -/// \brief Sets the information of an object. -/// \param object The object to set the information of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_info The new information of the object. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -IKA_API void ikarus_object_set_info(IkarusObject const * object, char const * new_info, IkarusErrorData * error_out); - -/// \brief Compares two objects for equality. -/// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the -/// same object. -/// \param lhs The left hand side object. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param rhs The right hand side object. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return True if the objects are equal, false otherwise. -IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out); - /// \brief Visits an object. Calling the appropriate function for the object's type. /// \param object The object to visit. /// \param blueprint_visitor The function to call if the object is a blueprint. Skipped if null. @@ -84,9 +28,9 @@ IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const /// \param error_out \see errors.h IKA_API void ikarus_object_visit( IkarusObject * object, - void (*blueprint_visitor)(struct IkarusBlueprint *, void *), - void (*property_visitor)(struct IkarusProperty *, void *), - void (*entity_visitor)(struct IkarusEntity *, void *), + void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *), + void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *), + void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *), void * data, IkarusErrorData * error_out ); @@ -94,9 +38,9 @@ IKA_API void ikarus_object_visit( /// \see ikarus_object_visit IKA_API void ikarus_object_visit_const( IkarusObject const * object, - void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), - void (*property_visitor)(struct IkarusProperty const *, void *), - void (*entity_visitor)(struct IkarusEntity const *, void *), + void (*blueprint_visitor)(struct IkarusBlueprint const *, IkarusErrorData * error_out, void *), + void (*property_visitor)(struct IkarusProperty const *, IkarusErrorData * error_out, void *), + void (*entity_visitor)(struct IkarusEntity const *, IkarusErrorData * error_out, void *), void * data, IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index 7a94079..589854f 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -70,7 +70,6 @@ IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The type info of the property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Gets the source of a property. @@ -80,7 +79,7 @@ IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * prope /// \param error_out \see errors.h /// \return The source of the property or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property, IkarusErrorData * error_out); +IKA_API struct IkarusPropertyScope const * ikarus_property_get_scope(IkarusProperty const * property, IkarusErrorData * error_out); /// \brief Gets the default value of a property. /// \param property The property to get the type info of. diff --git a/include/ikarus/objects/properties/property_source.h b/include/ikarus/objects/properties/property_scope.h similarity index 86% rename from include/ikarus/objects/properties/property_source.h rename to include/ikarus/objects/properties/property_scope.h index f1da29d..a211e67 100644 --- a/include/ikarus/objects/properties/property_source.h +++ b/include/ikarus/objects/properties/property_scope.h @@ -11,7 +11,7 @@ IKARUS_BEGIN_HEADER -struct IkarusPropertySource; +struct IkarusPropertyScope; /// \brief Creates an blueprint property source. /// \param blueprint The blueprint to create the property source for. @@ -20,7 +20,7 @@ struct IkarusPropertySource; /// \param error_out \see errors.h /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * +IKA_API struct IkarusPropertyScope * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint, IkarusErrorData * error_out); /// \brief Creates an entity property source. @@ -30,7 +30,7 @@ ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint, Ikar /// \param error_out \see errors.h /// \return The created property source or null if an error occurs. /// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity, IkarusErrorData * error_out); +IKA_API struct IkarusPropertyScope * ikarus_property_source_create_entity(struct IkarusEntity * entity, IkarusErrorData * error_out); /// \brief Visits a property source, calling the appropriate callback. /// \param property_source The property source to visit. @@ -41,7 +41,7 @@ IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struc /// \param user_data User data to pass to the callbacks. /// \param error_out \see errors.h IKA_API void ikarus_property_source_visit( - struct IkarusPropertySource * property_source, + struct IkarusPropertyScope * property_source, void (*blueprint_visitor)(struct IkarusBlueprint *, void *), void (*entity_visitor)(struct IkarusEntity *, void *), void * user_data, @@ -50,7 +50,7 @@ IKA_API void ikarus_property_source_visit( /// \see ikarus_property_source_visit IKA_API void ikarus_property_source_visit_const( - struct IkarusPropertySource const * property_source, + struct IkarusPropertyScope const * property_source, void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), void (*entity_visitor)(struct IkarusEntity const *, void *), void * user_data, diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 117909e..f1f083a 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -80,29 +80,6 @@ IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_n /// \remark Ownership remains with libikarus, must not be freed. IKA_API char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out); -/// \brief Gets the blueprints of a project. -/// \param project The project to get the blueprints of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param blueprints_out The buffer to write the blueprints to. -/// \pre \li Must not be null. -/// \param blueprints_out_size The size of the buffer. -/// \param error_out \see errors.h -IKA_API void ikarus_project_get_blueprints( - IkarusProject * project, - struct IkarusBlueprint ** blueprints_out, - size_t blueprints_out_size, - IkarusErrorData * error_out -); - -/// \brief Gets the number of blueprints of a project. -/// \param project The project to get the number of blueprints of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); - /// \brief Gets the entities of a project. /// \param project The project to get the entities of. /// \pre \li Must not be null. @@ -126,6 +103,71 @@ IKA_API void ikarus_project_get_entities( /// \return The number of entities or undefined if an error occurs. IKA_API size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusErrorData * error_out); +/// \brief Gets the blueprints of a project. +/// \param project The project to get the blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprints_out The buffer to write the blueprints to. +/// \pre \li Must not be null. +/// \param blueprints_out_size The size of the buffer. +/// \param error_out \see errors.h +IKA_API void ikarus_project_get_blueprints( + IkarusProject * project, + struct IkarusBlueprint ** blueprints_out, + size_t blueprints_out_size, + IkarusErrorData * error_out +); + +/// \brief Gets the number of blueprints of a project. +/// \param project The project to get the number of blueprints of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param error_out \see errors.h +/// \return The number of blueprints or undefined if an error occurs. +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); + +/// \brief Finds an entity by a given name. +/// \param project The project to search. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name to search for. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param error_out \see errors.h +/// \return The entity with the given name or null if none was found. +IKA_API struct IkarusEntity* get_entity_by_name( + IkarusProject const * project, + char const * name, + IkarusErrorData * error_out +); + +/// \brief Finds a property by a given name. +/// \param project The project to search. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param scope The scope of the property. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \remark Property names are unique only within their scope. +/// \param name The name to search for. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param error_out \see errors.h +/// \return The property with the given name or null if none was found. +IKA_API struct IkarusProperty * +get_property_by_name(IkarusProject const * project, struct IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out); + +/// \brief Finds a blueprint by a given name. +/// \param project The project to search. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name to search for. +/// \pre \li Must not be null. +/// \pre \li Must not be empty. +/// \param error_out \see errors.h +/// \return The blueprint with the given name or null if none was found. +IKA_API struct IkarusBlueprint * get_blueprints_by_name(IkarusProject const * project, char const * name, IkarusErrorData * error_out); + IKARUS_END_HEADER /// @} diff --git a/src/errors.cpp b/src/ikarus/errors.cpp similarity index 98% rename from src/errors.cpp rename to src/ikarus/errors.cpp index 4441fe9..d98b28c 100644 --- a/src/errors.cpp +++ b/src/ikarus/errors.cpp @@ -1,13 +1,13 @@ #include "ikarus/errors.h" -#include "cppbase/functional.hpp" - #include #include #include +#include + char const * get_error_info_name(IkarusErrorInfo info) { switch (info) { case IkarusErrorInfo_None: return "None"; diff --git a/src/errors.hpp b/src/ikarus/errors.hpp similarity index 100% rename from src/errors.hpp rename to src/ikarus/errors.hpp diff --git a/src/global.cpp b/src/ikarus/global.cpp similarity index 100% rename from src/global.cpp rename to src/ikarus/global.cpp diff --git a/src/id.cpp b/src/ikarus/id.cpp similarity index 93% rename from src/id.cpp rename to src/ikarus/id.cpp index 4269215..031c490 100644 --- a/src/id.cpp +++ b/src/ikarus/id.cpp @@ -3,7 +3,7 @@ #include constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; -constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; +constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; auto ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); diff --git a/src/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp similarity index 83% rename from src/objects/blueprint.cpp rename to src/ikarus/objects/blueprint.cpp index e5f3f0c..326129d 100644 --- a/src/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -1,24 +1,23 @@ #include "ikarus/objects/blueprint.h" -#include "ikarus/objects/properties/property.h" -#include "objects/blueprint.hpp" - #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusObject{project, id} {} IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - 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_FAIL_IF_NAME_INVALID(name, project, nullptr, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, @@ -50,6 +49,18 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * erro blueprint->project->uncache(blueprint); } +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, @@ -147,17 +158,3 @@ size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprin return ret; } - -// No existence checks here for performance reasons. All methods on IkarusObject perform this check anyway. - -IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(blueprint, nullptr); - - return static_cast(blueprint); -} - -IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(blueprint, nullptr); - - return static_cast(blueprint); -} diff --git a/src/objects/blueprint.hpp b/src/ikarus/objects/blueprint.hpp similarity index 71% rename from src/objects/blueprint.hpp rename to src/ikarus/objects/blueprint.hpp index 61aca64..30c57b0 100644 --- a/src/objects/blueprint.hpp +++ b/src/ikarus/objects/blueprint.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include struct IkarusBlueprint : IkarusObject { +public: IkarusBlueprint(struct IkarusProject * project, IkarusId id); IkarusBlueprint(IkarusBlueprint const &) = default; @@ -12,4 +13,9 @@ struct IkarusBlueprint : IkarusObject { IkarusBlueprint & operator=(IkarusBlueprint &&) = default; ~IkarusBlueprint() override = default; + +public: + inline std::string_view get_table_name() const noexcept override { + return "blueprints"; + } }; diff --git a/src/objects/entity.cpp b/src/ikarus/objects/entity.cpp similarity index 88% rename from src/objects/entity.cpp rename to src/ikarus/objects/entity.cpp index 6c1839a..94e8b75 100644 --- a/src/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -1,19 +1,18 @@ #include "entity.hpp" -#include "values/value.hpp" - #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - 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_FAIL_IF_NAME_INVALID(name, project, nullptr, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, @@ -21,9 +20,9 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * "failed to create entity: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Entity, name, "")); + TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?, ?, ?)", IkarusObjectType_Entity)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Entity); - TRY(db->execute("INSERT INTO `entities`(`id`) VALUES(?)", id)); + TRY(db->execute("INSERT INTO `entities`(`id`, `name`) VALUES(?)", id, name)); return cppbase::ok(id); }) ); @@ -45,6 +44,18 @@ void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { entity->project->uncache(entity); } +IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity, IkarusErrorData * error_out) { + return ikarus::util::object_get_project(entity, error_out); +} + +char const * ikarus_entity_get_name(IkarusEntity const * entity, IkarusErrorData * error_out) { + return ikarus::util::object_get_name(entity, error_out); +} + +void ikarus_entity_set_name(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) { + ikarus::util::object_set_name(entity, name, error_out); +} + bool ikarus_entity_is_linked_to_blueprint( IkarusEntity const * entity, struct IkarusBlueprint const * blueprint, @@ -273,17 +284,3 @@ void ikarus_entity_set_value( ) ); } - -// No existence checks here for performance reasons. All methods on IkarusObject perform this check anyway. - -struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - - return static_cast(entity); -} - -struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - - return static_cast(entity); -} diff --git a/src/objects/entity.hpp b/src/ikarus/objects/entity.hpp similarity index 72% rename from src/objects/entity.hpp rename to src/ikarus/objects/entity.hpp index 92cf4c6..586c88f 100644 --- a/src/objects/entity.hpp +++ b/src/ikarus/objects/entity.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include struct IkarusEntity : IkarusObject { +public: inline IkarusEntity(struct IkarusProject * project, IkarusId id): IkarusObject{project, id} {} @@ -13,4 +14,9 @@ struct IkarusEntity : IkarusObject { IkarusEntity & operator=(IkarusEntity &&) = default; ~IkarusEntity() override = default; + +public: + inline std::string_view get_table_name() const noexcept override { + return "entities"; + } }; diff --git a/src/ikarus/objects/object.cpp b/src/ikarus/objects/object.cpp new file mode 100644 index 0000000..d233783 --- /dev/null +++ b/src/ikarus/objects/object.cpp @@ -0,0 +1,93 @@ +#include "object.hpp" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): + project{project}, + id{id} {} + +void ikarus_object_visit( + IkarusObject * object, + void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *), + void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *), + void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *), + void * data, + IkarusErrorData * error_out +) { + struct Data { + void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *); + void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *); + void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *); + void * data; + }; + + Data passthru_data{blueprint_visitor, property_visitor, entity_visitor, data}; + + ikarus_object_visit_const( + object, + [](IkarusBlueprint const * blueprint, IkarusErrorData * error_out, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->blueprint_visitor(const_cast(blueprint), error_out, passthru_data->data); + }, + [](IkarusProperty const * property, IkarusErrorData * error_out, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->property_visitor(const_cast(property), error_out, passthru_data->data); + }, + [](IkarusEntity const * entity, IkarusErrorData * error_out, void * data) { + auto const * passthru_data = static_cast(data); + passthru_data->entity_visitor(const_cast(entity), error_out, passthru_data->data); + }, + &passthru_data, + error_out + ); +} + +void ikarus_object_visit_const( + IkarusObject const * object, + void (*blueprint_visitor)(struct IkarusBlueprint const *, IkarusErrorData * error_out, void *), + void (*property_visitor)(struct IkarusProperty const *, IkarusErrorData * error_out, void *), + void (*entity_visitor)(struct IkarusEntity const *, IkarusErrorData * error_out, void *), + void * data, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(object, ); + IKARUS_FAIL_IF_OBJECT_MISSING(object, ); + + switch (ikarus_id_get_object_type(object->id)) { + case IkarusObjectType_Entity: { + auto const * entity = dynamic_cast(object); + IKARUS_FAIL_IF(entity == nullptr, , "object with entity id wasn't a entity", IkarusErrorInfo_LibIkarus_InvalidState); + entity_visitor(entity, error_out, data); + } + case IkarusObjectType_Blueprint: { + auto const * blueprint = dynamic_cast(object); + IKARUS_FAIL_IF(blueprint == nullptr, , "object with blueprint id wasn't a blueprint", IkarusErrorInfo_LibIkarus_InvalidState); + blueprint_visitor(blueprint, error_out, data); + } + case IkarusObjectType_Property: { + auto const * property = dynamic_cast(object); + IKARUS_FAIL_IF(property == nullptr, , "object with property id wasn't a property", IkarusErrorInfo_LibIkarus_InvalidState); + property_visitor(property, error_out, data); + } + default: { + IKARUS_FAIL( + , + fmt::format("unknown object type: {}", ikarus_object_type_to_string(ikarus_id_get_object_type(object->id))), + IkarusErrorInfo_LibIkarus_InvalidState + ); + } + } + + // not needed, but a good delineation of our intention if this function gets extended + IKARUS_FAIL_IF_ERROR() +} diff --git a/src/objects/object.hpp b/src/ikarus/objects/object.hpp similarity index 82% rename from src/objects/object.hpp rename to src/ikarus/objects/object.hpp index 56e141b..20eed59 100644 --- a/src/objects/object.hpp +++ b/src/ikarus/objects/object.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include struct IkarusObject { @@ -14,6 +16,9 @@ public: virtual ~IkarusObject() = default; +public: + virtual std::string_view get_table_name() const noexcept = 0; + public: struct IkarusProject * project; IkarusId id; diff --git a/src/objects/object_type.cpp b/src/ikarus/objects/object_type.cpp similarity index 100% rename from src/objects/object_type.cpp rename to src/ikarus/objects/object_type.cpp diff --git a/src/objects/properties/number_property.cpp b/src/ikarus/objects/properties/number_property.cpp similarity index 70% rename from src/objects/properties/number_property.cpp rename to src/ikarus/objects/properties/number_property.cpp index b94429d..43a966c 100644 --- a/src/objects/properties/number_property.cpp +++ b/src/ikarus/objects/properties/number_property.cpp @@ -1,13 +1,11 @@ -#include "ikarus/objects/properties/number_property.h" +#include "number_property.hpp" #include #include - -#include -#include -#include -#include +#include +#include +#include IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} @@ -15,11 +13,10 @@ IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, IkarusId id) IkarusNumberProperty * ikarus_number_property_create( struct IkarusProject * project, char const * name, - struct IkarusPropertySource * property_source, - struct IkarusNumberValue * default_value, + struct IkarusPropertyScope * property_source, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, default_value, error_out); + return ikarus::util::create_property(project, name, property_source, error_out); } IkarusNumberValue * ikarus_number_property_get_default_value(IkarusNumberProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/number_property.hpp b/src/ikarus/objects/properties/number_property.hpp similarity index 77% rename from src/objects/properties/number_property.hpp rename to src/ikarus/objects/properties/number_property.hpp index 22ca3d3..eddb189 100644 --- a/src/objects/properties/number_property.hpp +++ b/src/ikarus/objects/properties/number_property.hpp @@ -1,9 +1,8 @@ #pragma once +#include #include - -#include -#include +#include struct IkarusNumberProperty : IkarusProperty { public: diff --git a/src/objects/properties/property.cpp b/src/ikarus/objects/properties/property.cpp similarity index 81% rename from src/objects/properties/property.cpp rename to src/ikarus/objects/properties/property.cpp index d3c27be..f45adbb 100644 --- a/src/objects/properties/property.cpp +++ b/src/ikarus/objects/properties/property.cpp @@ -2,14 +2,13 @@ #include +#include #include +#include #include - -#include -#include -#include -#include -#include +#include +#include +#include IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): IkarusObject{project, id}, @@ -29,6 +28,18 @@ IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * property->project->uncache(property); } +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); +} + IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(property, IkarusPropertyType_Toggle); IKARUS_FAIL_IF_OBJECT_MISSING(property, IkarusPropertyType_Toggle); @@ -44,7 +55,7 @@ IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, Ika return static_cast(ret); } -IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property, IkarusErrorData * error_out) { +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); @@ -57,8 +68,8 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p ); switch (ikarus_id_get_object_type(source)) { - case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->project->get_blueprint(source)}; - case IkarusObjectType_Entity: return new IkarusPropertySource{property->project->get_entity(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, diff --git a/src/objects/properties/property.hpp b/src/ikarus/objects/properties/property.hpp similarity index 79% rename from src/objects/properties/property.hpp rename to src/ikarus/objects/properties/property.hpp index 3a95d6b..5c37d7c 100644 --- a/src/objects/properties/property.hpp +++ b/src/ikarus/objects/properties/property.hpp @@ -2,7 +2,7 @@ #include -#include +#include struct IkarusProperty : IkarusObject { public: @@ -19,6 +19,11 @@ public: ~IkarusProperty() override = default; +public: + inline std::string_view get_table_name() const noexcept override { + return "properties"; + } + public: Data data; }; diff --git a/src/objects/properties/property_source.cpp b/src/ikarus/objects/properties/property_scope.cpp similarity index 58% rename from src/objects/properties/property_source.cpp rename to src/ikarus/objects/properties/property_scope.cpp index 3e31bb3..a0b9ade 100644 --- a/src/objects/properties/property_source.cpp +++ b/src/ikarus/objects/properties/property_scope.cpp @@ -1,17 +1,16 @@ -#include "property_source.hpp" +#include "property_scope.hpp" -#include - -#include -#include #include -IkarusPropertySource::IkarusPropertySource(Data data): +#include +#include + +IkarusPropertyScope::IkarusPropertyScope(Data data): data{data} {} -IkarusId IkarusPropertySource::get_id() const { - return boost::variant2::visit( - cppbase::overloaded { +IkarusId IkarusPropertyScope::get_id() const { + return std::visit( + cppbase::overloaded{ [](IkarusBlueprint const * blueprint) { return blueprint->id; }, [](IkarusEntity const * entity) { return entity->id; } }, @@ -19,37 +18,37 @@ IkarusId IkarusPropertySource::get_id() const { ); } -IkarusPropertySource * ikarus_property_source_create_blueprint(IkarusBlueprint * blueprint) { - return new IkarusPropertySource{blueprint}; +IkarusPropertyScope * ikarus_property_source_create_blueprint(IkarusBlueprint * blueprint) { + return new IkarusPropertyScope{blueprint}; } -IkarusPropertySource * ikarus_property_source_create_entity(IkarusEntity * entity) { - return new IkarusPropertySource{entity}; +IkarusPropertyScope * ikarus_property_source_create_entity(IkarusEntity * entity) { + return new IkarusPropertyScope{entity}; } void ikarus_property_source_visit( - struct IkarusPropertySource * property_source, + struct IkarusPropertyScope * property_source, void (*blueprint_visitor)(struct IkarusBlueprint *, void *), void (*entity_visitor)(struct IkarusEntity *, void *), void * user_data ) { - boost::variant2::visit( - boost::make_overloaded_function( + std::visit( + cppbase::overloaded{ [blueprint_visitor, user_data](IkarusBlueprint * blueprint) { blueprint_visitor(blueprint, user_data); }, [entity_visitor, user_data](IkarusEntity * entity) { entity_visitor(entity, user_data); } - ), + }, property_source->data ); } void ikarus_property_source_visit_const( - struct IkarusPropertySource const * property_source, + struct IkarusPropertyScope const * property_source, void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), void (*entity_visitor)(struct IkarusEntity const *, void *), void * user_data ) { - boost::variant2::visit( - boost::make_overloaded_function( + std::visit( + cppbase::overloaded( [blueprint_visitor, user_data](IkarusBlueprint const * blueprint) { blueprint_visitor(blueprint, user_data); }, [entity_visitor, user_data](IkarusEntity const * entity) { entity_visitor(entity, user_data); } ), diff --git a/src/ikarus/objects/properties/property_scope.hpp b/src/ikarus/objects/properties/property_scope.hpp new file mode 100644 index 0000000..ab8832a --- /dev/null +++ b/src/ikarus/objects/properties/property_scope.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include +#include + +#define IKARUS_FAIL_IF_PROPERTY_SCOPE_INVALID(scope, ret) \ + std::visit( \ + cppbase::overloaded{[error_out](auto const * object) { \ + IKARUS_FAIL_IF_NULL(object, ); \ + IKARUS_FAIL_IF_OBJECT_MISSING(object, ); \ + }}, \ + scope->data \ + ); \ + \ + IKARUS_FAIL_IF_ERROR(nullptr); + +struct IkarusPropertyScope { +public: + using Data = std::variant; + +public: + explicit IkarusPropertyScope(Data data); + + IkarusPropertyScope(IkarusPropertyScope const &) = default; + IkarusPropertyScope(IkarusPropertyScope &&) = default; + + IkarusPropertyScope & operator=(IkarusPropertyScope const &) = default; + IkarusPropertyScope & operator=(IkarusPropertyScope &&) = default; + + virtual ~IkarusPropertyScope() = default; + +public: + [[nodiscard]] IkarusId get_id() const; + +public: + Data data; +}; diff --git a/src/objects/properties/text_property.cpp b/src/ikarus/objects/properties/text_property.cpp similarity index 77% rename from src/objects/properties/text_property.cpp rename to src/ikarus/objects/properties/text_property.cpp index c7541ff..6aaf483 100644 --- a/src/objects/properties/text_property.cpp +++ b/src/ikarus/objects/properties/text_property.cpp @@ -3,10 +3,9 @@ #include #include - -#include -#include -#include +#include +#include +#include IkarusTextProperty::IkarusTextProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} @@ -14,11 +13,10 @@ IkarusTextProperty::IkarusTextProperty(IkarusProject * project, IkarusId id): IkarusTextProperty * ikarus_text_property_create( struct IkarusProject * project, char const * name, - struct IkarusPropertySource * property_source, - struct IkarusTextValue * default_value, + struct IkarusPropertyScope * property_source, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, default_value, error_out); + return ikarus::util::create_property(project, name, property_source, error_out); } IkarusTextValue * ikarus_text_property_get_default_value(IkarusTextProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/text_property.hpp b/src/ikarus/objects/properties/text_property.hpp similarity index 77% rename from src/objects/properties/text_property.hpp rename to src/ikarus/objects/properties/text_property.hpp index a3b7e97..aca2cfa 100644 --- a/src/objects/properties/text_property.hpp +++ b/src/ikarus/objects/properties/text_property.hpp @@ -1,9 +1,8 @@ #pragma once +#include #include - -#include -#include +#include struct IkarusTextProperty : IkarusProperty { public: diff --git a/src/objects/properties/toggle_property.cpp b/src/ikarus/objects/properties/toggle_property.cpp similarity index 77% rename from src/objects/properties/toggle_property.cpp rename to src/ikarus/objects/properties/toggle_property.cpp index 2cde654..2449b39 100644 --- a/src/objects/properties/toggle_property.cpp +++ b/src/ikarus/objects/properties/toggle_property.cpp @@ -3,10 +3,9 @@ #include #include - -#include -#include -#include +#include +#include +#include IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): IkarusProperty{project, id, this} {} @@ -14,11 +13,10 @@ IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id) IkarusToggleProperty * ikarus_toggle_property_create( struct IkarusProject * project, char const * name, - struct IkarusPropertySource * property_source, - struct IkarusToggleValue * default_value, + struct IkarusPropertyScope * property_source, IkarusErrorData * error_out ) { - return ikarus::util::create_property(project, name, property_source, default_value, error_out); + return ikarus::util::create_property(project, name, property_source, error_out); } IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, IkarusErrorData * error_out) { diff --git a/src/objects/properties/toggle_property.hpp b/src/ikarus/objects/properties/toggle_property.hpp similarity index 77% rename from src/objects/properties/toggle_property.hpp rename to src/ikarus/objects/properties/toggle_property.hpp index 247cbcb..bf53c19 100644 --- a/src/objects/properties/toggle_property.hpp +++ b/src/ikarus/objects/properties/toggle_property.hpp @@ -1,9 +1,8 @@ #pragma once +#include #include - -#include -#include +#include struct IkarusToggleProperty : IkarusProperty { public: diff --git a/src/objects/properties/util.hpp b/src/ikarus/objects/properties/util.hpp similarity index 73% rename from src/objects/properties/util.hpp rename to src/ikarus/objects/properties/util.hpp index d9321c7..092296e 100644 --- a/src/objects/properties/util.hpp +++ b/src/ikarus/objects/properties/util.hpp @@ -4,48 +4,40 @@ #include +#include #include - -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include namespace ikarus::util { template T * create_property( struct IkarusProject * project, char const * name, - struct IkarusPropertySource * property_source, - typename T::value_type * default_value, + struct IkarusPropertyScope * property_scope, IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NULL(name, nullptr); - IKARUS_FAIL_IF_NULL(property_source, nullptr); - IKARUS_FAIL_IF( - cppbase::is_empty_or_blank(name), - nullptr, - fmt::format("{} name cannot be empty or blank", boost::typeindex::type_id().pretty_name()), - IkarusErrorInfo_Client_InvalidInput - ) - IKARUS_FAIL_IF_NULL(default_value, nullptr); + IKARUS_FAIL_IF_NULL(property_scope, nullptr); + IKARUS_FAIL_IF_PROPERTY_SCOPE_INVALID(property_scope, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, project, property_scope, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, nullptr, "failed to create property: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->transact([name, property_source, default_value](auto * db) -> cppbase::Result { + project->db->transact([name, property_scope](auto * db) -> cppbase::Result { TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Property, name, "")); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Property); TRY(db->execute( - "INSERT INTO `properties`(`id`, `type`, `source`, `default_value`) VALUES(?, ?, ?, ?)", + "INSERT INTO `properties`(`id`, `type`, `source`) VALUES(?, ?, ?)", id, T::PropertyType, - property_source->get_id(), - boost::json::serialize(default_value->to_json()) + property_scope->get_id() )); return cppbase::ok(id); }) diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp new file mode 100644 index 0000000..29857cf --- /dev/null +++ b/src/ikarus/objects/util.hpp @@ -0,0 +1,125 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ikarus::util { + +template +[[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 +[[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(fmt::format("SELECT `name` FROM `{}` WHERE `id` = ?", object->get_table_name()), object->id) + ); + + return strdup(ret.data()); +} + +[[nodiscard]] inline bool name_is_unique( + IkarusProject const * project, + std::string_view name, + [[maybe_unused]] IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +) { + IKARUS_VTRYRV_OR_FAIL( + bool const exists, + false, + "unable to check if blueprint name is unique", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT EXISTS(SELECT 1 FROM `blueprints` WHERE `name` = ?)", name) + ); + + return exists; +} + +[[nodiscard]] inline bool name_is_unique( + IkarusProject const * project, + std::string_view name, + [[maybe_unused]] IkarusEntity const * entity, + IkarusErrorData * error_out +) { + IKARUS_VTRYRV_OR_FAIL( + bool const exists, + false, + "unable to check if entity name is unique", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT EXISTS(SELECT 1 FROM `entities` WHERE `name` = ?)", name) + ); + + return exists; +} + +[[nodiscard]] inline bool +name_is_unique(IkarusProject const * project, std::string_view name, IkarusPropertyScope const * scope, IkarusErrorData * error_out) { + IKARUS_VTRYRV_OR_FAIL( + bool const exists, + false, + "unable to check if property name is unique", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT EXISTS(SELECT 1 FROM `properties` WHERE `name` = ? AND `scope` = ?)", name, scope->get_id()) + ); + + return exists; +} + +[[nodiscard]] inline bool +name_is_unique(IkarusProject * project, std::string_view name, IkarusProperty const * property, IkarusErrorData * error_out) { + std::unique_ptr scope{ikarus_property_get_scope(property, error_out)}; + IKARUS_FAIL_IF_ERROR(false); + + return name_is_unique(project, name, scope.get(), error_out); +} + +#define IKARUS_FAIL_IF_NAME_INVALID(name, project, object, ret, ...) \ + IKARUS_FAIL_IF_NULL(name, ret); \ + IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); \ + IKARUS_FAIL_IF( \ + !ikarus::util::name_is_unique(project, name, object, error_out), \ + ret, \ + "name must be unique", \ + IkarusErrorInfo_Client_InvalidInput \ + ); \ + IKARUS_FAIL_IF_ERROR(ret); + +template +void object_set_name(O * object, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(object, ); + IKARUS_FAIL_IF_OBJECT_MISSING(object, ); + IKARUS_FAIL_IF_NULL(name, ); + IKARUS_FAIL_IF_NAME_INVALID(name, object->project, object, ); + + IKARUS_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 diff --git a/src/persistence/migrations.hpp b/src/ikarus/persistence/migrations.hpp similarity index 90% rename from src/persistence/migrations.hpp rename to src/ikarus/persistence/migrations.hpp index c777e84..a46d1ee 100644 --- a/src/persistence/migrations.hpp +++ b/src/ikarus/persistence/migrations.hpp @@ -8,7 +8,7 @@ #include namespace ikarus { -CPPBASE_ASSET(m0_initial_layout, "persistence/migrations/m0_initial_layout.sql"); +CPPBASE_ASSET(m0_initial_layout, "ikarus/persistence/migrations/m0_initial_layout.sql"); class Migration : public sqlitecpp::Migration { public: diff --git a/src/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql similarity index 61% rename from src/persistence/migrations/m0_initial_layout.sql rename to src/ikarus/persistence/migrations/m0_initial_layout.sql index 99ba61b..0031cee 100644 --- a/src/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -2,36 +2,29 @@ CREATE TABLE `objects` ( `do_not_access_rowid_alias` INTEGER PRIMARY KEY, `type` INT NOT NULL, - `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`type` << 56)) VIRTUAL, - `name` TEXT NOT NULL, - `information` TEXT NOT NULL + `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`type` << 56)) VIRTUAL UNIQUE ) STRICT; CREATE UNIQUE INDEX `object_id` ON `objects` (`id`); CREATE INDEX `object_type` ON `objects` (`type`); -CREATE VIRTUAL TABLE `objects_fts` USING fts5 -( - `name`, - `information`, - content='objects', - content_rowid='id', - tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" -); - CREATE TABLE `entities` ( - `id` INT, + `id` INT, + `name` TEXT NOT NULL, PRIMARY KEY (`id`), + UNIQUE (`name`), FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; CREATE TABLE `blueprints` ( - `id` INT, + `id` INT, + `name` TEXT NOT NULL, PRIMARY KEY (`id`), + UNIQUE (`name`), FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; @@ -40,23 +33,22 @@ CREATE TABLE `entity_blueprint_links` `entity` INT NOT NULL, `blueprint` INT NOT NULL, - PRIMARY KEY (`entity`), - UNIQUE (`entity`, `blueprint`), + PRIMARY KEY (`entity`, `blueprint`), FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, FOREIGN KEY (`blueprint`) REFERENCES `blueprints` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; -CREATE INDEX `entity_blueprints_blueprint` ON `entity_blueprint_links` (`blueprint`); - CREATE TABLE `properties` ( `id` INT, + `name` TEXT NOT NULL, `type` INT NOT NULL, `default_value` TEXT NOT NULL, `settings` TEXT NOT NULL, `source` INT NOT NULL, PRIMARY KEY (`id`), + UNIQUE(`source`, `name`), FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE, FOREIGN KEY (`source`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; @@ -64,24 +56,6 @@ CREATE TABLE `properties` CREATE INDEX `properties_type` ON `properties` (`type`); CREATE INDEX `properties_source` ON `properties` (`source`); -CREATE -VIRTUAL TABLE `property_default_value_fts` USING fts5 -( - `default_value`, - content='properties', - content_rowid='object_id', - tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" -); - -CREATE -VIRTUAL TABLE `property_settings_fts` USING fts5 -( - `settings`, - content='properties', - content_rowid='object_id', - tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" -); - CREATE TABLE `values` ( `entity` INT NOT NULL, @@ -92,11 +66,3 @@ CREATE TABLE `values` FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, FOREIGN KEY (`property`) REFERENCES `properties` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; - -CREATE -VIRTUAL TABLE `values_fts` USING fts5 -( - `value`, - content='values', - tokenize="unicode61 remove_diacritics 2 tokenchars '-_'" -); diff --git a/src/persistence/project.cpp b/src/ikarus/persistence/project.cpp similarity index 95% rename from src/persistence/project.cpp rename to src/ikarus/persistence/project.cpp index 41ddbf7..6c460f7 100644 --- a/src/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -4,14 +4,14 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include IkarusProject::IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db): name{name}, diff --git a/src/persistence/project.hpp b/src/ikarus/persistence/project.hpp similarity index 100% rename from src/persistence/project.hpp rename to src/ikarus/persistence/project.hpp diff --git a/src/values/entity_property_value.cpp b/src/ikarus/values/entity_property_value.cpp similarity index 90% rename from src/values/entity_property_value.cpp rename to src/ikarus/values/entity_property_value.cpp index d1580ea..165383d 100644 --- a/src/values/entity_property_value.cpp +++ b/src/ikarus/values/entity_property_value.cpp @@ -1,6 +1,6 @@ -#include "values/entity_property_value.hpp" +#include "entity_property_value.hpp" -#include +#include IkarusEntity const * ikarus_entity_property_value_get_entity(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(value, nullptr); diff --git a/src/values/entity_property_value.hpp b/src/ikarus/values/entity_property_value.hpp similarity index 100% rename from src/values/entity_property_value.hpp rename to src/ikarus/values/entity_property_value.hpp diff --git a/src/values/number_value.cpp b/src/ikarus/values/number_value.cpp similarity index 96% rename from src/values/number_value.cpp rename to src/ikarus/values/number_value.cpp index c339149..37eab49 100644 --- a/src/values/number_value.cpp +++ b/src/ikarus/values/number_value.cpp @@ -2,8 +2,8 @@ #include -#include -#include +#include +#include IkarusNumberValue::IkarusNumberValue(): IkarusValue{this} {} diff --git a/src/values/number_value.hpp b/src/ikarus/values/number_value.hpp similarity index 94% rename from src/values/number_value.hpp rename to src/ikarus/values/number_value.hpp index 8644d3e..6c6cb70 100644 --- a/src/values/number_value.hpp +++ b/src/ikarus/values/number_value.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include struct IkarusNumberValue : IkarusValue { public: diff --git a/src/values/text_value.cpp b/src/ikarus/values/text_value.cpp similarity index 96% rename from src/values/text_value.cpp rename to src/ikarus/values/text_value.cpp index 64006ac..8235c6d 100644 --- a/src/values/text_value.cpp +++ b/src/ikarus/values/text_value.cpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include IkarusTextValue::IkarusTextValue(): IkarusValue{this} {} diff --git a/src/values/text_value.hpp b/src/ikarus/values/text_value.hpp similarity index 94% rename from src/values/text_value.hpp rename to src/ikarus/values/text_value.hpp index 40d3945..512bb8d 100644 --- a/src/values/text_value.hpp +++ b/src/ikarus/values/text_value.hpp @@ -2,7 +2,7 @@ #include -#include +#include struct IkarusTextValue : IkarusValue { public: diff --git a/src/values/toggle_value.cpp b/src/ikarus/values/toggle_value.cpp similarity index 96% rename from src/values/toggle_value.cpp rename to src/ikarus/values/toggle_value.cpp index dabbe00..7bec1f2 100644 --- a/src/values/toggle_value.cpp +++ b/src/ikarus/values/toggle_value.cpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include IkarusToggleValue::IkarusToggleValue(): IkarusValue{this} {} diff --git a/src/values/toggle_value.hpp b/src/ikarus/values/toggle_value.hpp similarity index 94% rename from src/values/toggle_value.hpp rename to src/ikarus/values/toggle_value.hpp index 20a5dd6..6882ecc 100644 --- a/src/values/toggle_value.hpp +++ b/src/ikarus/values/toggle_value.hpp @@ -2,7 +2,7 @@ #include -#include +#include struct IkarusToggleValue : IkarusValue { public: diff --git a/src/values/value.cpp b/src/ikarus/values/value.cpp similarity index 96% rename from src/values/value.cpp rename to src/ikarus/values/value.cpp index 812b2c8..bbea7f3 100644 --- a/src/values/value.cpp +++ b/src/ikarus/values/value.cpp @@ -10,11 +10,10 @@ #include #include - -#include -#include -#include -#include +#include +#include +#include +#include IkarusValue::IkarusValue(Data data): data(data) {} diff --git a/src/values/value.hpp b/src/ikarus/values/value.hpp similarity index 91% rename from src/values/value.hpp rename to src/ikarus/values/value.hpp index 94b423d..1e5791b 100644 --- a/src/values/value.hpp +++ b/src/ikarus/values/value.hpp @@ -5,8 +5,8 @@ #include -#include -#include +#include +#include struct IkarusValue { public: @@ -41,7 +41,7 @@ struct fmt::formatter { return ctx.end(); } - constexpr static auto format([[maybe_unused]]IkarusValue::FromJsonError const & error, fmt::format_context & ctx) { + constexpr static auto format([[maybe_unused]] IkarusValue::FromJsonError const & error, fmt::format_context & ctx) { return fmt::format_to(ctx.out(), "unable to parse ikarus value JSON"); } }; diff --git a/src/values/value_base.hpp b/src/ikarus/values/value_base.hpp similarity index 100% rename from src/values/value_base.hpp rename to src/ikarus/values/value_base.hpp diff --git a/src/objects/object.cpp b/src/objects/object.cpp deleted file mode 100644 index e8a3353..0000000 --- a/src/objects/object.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "object.hpp" - -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include - -IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): - project{project}, - id{id} {} - -IkarusProject * ikarus_object_get_project(IkarusObject const * object, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(object, nullptr); - IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); - - return object->project; -} - -char const * ikarus_object_get_name(IkarusObject const * object, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(object, nullptr); - IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); - - IKARUS_VTRYRV_OR_FAIL( - std::string ret, - nullptr, - "unable to get object name: {}", - IkarusErrorInfo_Database_QueryFailed, - object->project->db->template query_one("SELECT `name` FROM `objects` WHERE `id` = ?", object->id) - ); - - return strdup(ret.data()); -} - -void ikarus_object_set_name(IkarusObject * 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(cppbase::is_empty_or_blank(name), , "name must not be empty", IkarusErrorInfo_Client_InvalidInput); - - IKARUS_TRYRV_OR_FAIL( - , - "unable to set object name: {}", - IkarusErrorInfo_Database_QueryFailed, - object->project->db->template execute("UPDATE `objects` SET `name` = ? WHERE `id` = ?", name, object->id) - ); -} - -char const * ikarus_object_get_information(IkarusObject const * object, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(object, nullptr); - IKARUS_FAIL_IF_OBJECT_MISSING(object, nullptr); - - IKARUS_VTRYRV_OR_FAIL( - std::string ret, - nullptr, - "unable to get object information: {}", - IkarusErrorInfo_Database_QueryFailed, - object->project->db->template query_one("SELECT `information` FROM `objects` WHERE `id` = ?", object->id) - ); - - return strdup(ret.data()); -} - -void ikarus_object_set_information(IkarusObject * object, char const * information, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(object, ); - IKARUS_FAIL_IF_OBJECT_MISSING(object, ); - IKARUS_FAIL_IF_NULL(information, ); - IKARUS_FAIL_IF(cppbase::is_empty_or_blank(information), , "information must not be empty", IkarusErrorInfo_Client_InvalidInput); - - IKARUS_TRYRV_OR_FAIL( - , - "unable to set object information: {}", - IkarusErrorInfo_Database_QueryFailed, - object->project->db->template execute("UPDATE `objects` SET `information` = ? WHERE `id` = ?", information, object->id) - ); -} - -bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(lhs, false); - IKARUS_FAIL_IF_OBJECT_MISSING(lhs, false); - IKARUS_FAIL_IF_NULL(rhs, false); - IKARUS_FAIL_IF_OBJECT_MISSING(rhs, false); - - return lhs->id == rhs->id; -} - -void ikarus_object_visit( - IkarusObject * object, - void (*blueprint_visitor)(struct IkarusBlueprint *, void *), - void (*property_visitor)(struct IkarusProperty *, void *), - void (*entity_visitor)(struct IkarusEntity *, void *), - void * data, - IkarusErrorData * error_out -) { - struct Data { - void (*blueprint_visitor)(struct IkarusBlueprint *, void *); - void (*property_visitor)(struct IkarusProperty *, void *); - void (*entity_visitor)(struct IkarusEntity *, void *); - void * data; - }; - - Data passthru_data{ - blueprint_visitor, - property_visitor, - entity_visitor, - data, - }; - - ikarus_object_visit_const( - object, - [](IkarusBlueprint const * blueprint, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->blueprint_visitor(const_cast(blueprint), passthru_data->data); - }, - [](IkarusProperty const * property, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->property_visitor(const_cast(property), passthru_data->data); - }, - [](IkarusEntity const * entity, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->entity_visitor(const_cast(entity), passthru_data->data); - }, - &passthru_data, - error_out - ); -} - -void ikarus_object_visit_const( - IkarusObject const * object, - void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), - void (*property_visitor)(struct IkarusProperty const *, void *), - void (*entity_visitor)(struct IkarusEntity const *, void *), - void * data, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(object, ); - IKARUS_FAIL_IF_OBJECT_MISSING(object, ); - - switch (ikarus_id_get_object_type(object->id)) { - case IkarusObjectType_Entity: { - auto const * entity = dynamic_cast(object); - IKARUS_FAIL_IF(entity == nullptr, , "object with entity id wasn't a entity", IkarusErrorInfo_LibIkarus_InvalidState); - entity_visitor(entity, data); - } - case IkarusObjectType_Blueprint: { - auto const * blueprint = dynamic_cast(object); - IKARUS_FAIL_IF(blueprint == nullptr, , "object with blueprint id wasn't a blueprint", IkarusErrorInfo_LibIkarus_InvalidState); - blueprint_visitor(blueprint, data); - } - case IkarusObjectType_Property: { - auto const * property = dynamic_cast(object); - IKARUS_FAIL_IF(property == nullptr, , "object with property id wasn't a property", IkarusErrorInfo_LibIkarus_InvalidState); - property_visitor(property, data); - } - default: { - IKARUS_FAIL( - , - fmt::format("unknown object type: {}", ikarus_object_type_to_string(ikarus_id_get_object_type(object->id))), - IkarusErrorInfo_LibIkarus_InvalidState - ); - } - } -} diff --git a/src/objects/properties/property_source.hpp b/src/objects/properties/property_source.hpp deleted file mode 100644 index 672021d..0000000 --- a/src/objects/properties/property_source.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include -#include - -struct IkarusPropertySource { -public: - using Data = boost::variant2::variant; - -public: - explicit IkarusPropertySource(Data data); - - IkarusPropertySource(IkarusPropertySource const &) = default; - IkarusPropertySource(IkarusPropertySource &&) = default; - - IkarusPropertySource & operator=(IkarusPropertySource const &) = default; - IkarusPropertySource & operator=(IkarusPropertySource &&) = default; - - virtual ~IkarusPropertySource() = default; - -public: - [[nodiscard]] IkarusId get_id() const; - -public: - Data data; -}; diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 77e7260..eadf323 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 77e726036b52fff6f82ff2d44081f05aee7408a8 +Subproject commit eadf3237bfa853763b332777e9dc0f16df8cca71 From 5201dfd20aa3039f4fdaa92d67d69126f36fb687 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 30 Jan 2024 09:33:59 +0100 Subject: [PATCH 129/166] fixup id generation Signed-off-by: Folling --- src/ikarus/id.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ikarus/id.cpp b/src/ikarus/id.cpp index 031c490..4269215 100644 --- a/src/ikarus/id.cpp +++ b/src/ikarus/id.cpp @@ -3,7 +3,7 @@ #include constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; -constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) - IKARUS_ID_OBJECT_TYPE_BITS; +constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; auto ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); From e6526ea83a8127d4cad8333b94dcc9ad4b6e7c60 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 30 Jan 2024 09:34:13 +0100 Subject: [PATCH 130/166] make project functions const where appropriate Signed-off-by: Folling --- include/ikarus/persistence/project.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index f1f083a..7847a78 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -89,7 +89,7 @@ IKA_API char const * ikarus_project_get_path(IkarusProject const * project, Ikar /// \param entities_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_entities( - IkarusProject * project, + IkarusProject const * project, struct IkarusEntity ** entities_out, size_t entities_out_size, IkarusErrorData * error_out @@ -101,7 +101,7 @@ IKA_API void ikarus_project_get_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of entities or undefined if an error occurs. -IKA_API size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Gets the blueprints of a project. /// \param project The project to get the blueprints of. @@ -112,7 +112,7 @@ IKA_API size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusEr /// \param blueprints_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_blueprints( - IkarusProject * project, + IkarusProject const * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size, IkarusErrorData * error_out @@ -135,11 +135,7 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The entity with the given name or null if none was found. -IKA_API struct IkarusEntity* get_entity_by_name( - IkarusProject const * project, - char const * name, - IkarusErrorData * error_out -); +IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject const * project, char const * name, IkarusErrorData * error_out); /// \brief Finds a property by a given name. /// \param project The project to search. From d629131b89a7a1d087d6109b8f96d9e65a9755e2 Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 30 Jan 2024 09:44:55 +0100 Subject: [PATCH 131/166] implement ikarus_project_get_*_by_name functions Signed-off-by: Folling --- include/ikarus/persistence/project.h | 12 +++--- src/ikarus/objects/blueprint.cpp | 2 +- src/ikarus/objects/entity.cpp | 2 +- src/ikarus/objects/properties/util.hpp | 2 +- src/ikarus/objects/util.hpp | 23 +++++----- src/ikarus/persistence/project.cpp | 58 ++++++++++++++++++++++++++ 6 files changed, 80 insertions(+), 19 deletions(-) diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 7847a78..d0116a4 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -89,7 +89,7 @@ IKA_API char const * ikarus_project_get_path(IkarusProject const * project, Ikar /// \param entities_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_entities( - IkarusProject const * project, + IkarusProject * project, struct IkarusEntity ** entities_out, size_t entities_out_size, IkarusErrorData * error_out @@ -112,7 +112,7 @@ IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, Ik /// \param blueprints_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_blueprints( - IkarusProject const * project, + IkarusProject * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size, IkarusErrorData * error_out @@ -124,7 +124,7 @@ IKA_API void ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject * project, IkarusErrorData * error_out); /// \brief Finds an entity by a given name. /// \param project The project to search. @@ -135,7 +135,7 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The entity with the given name or null if none was found. -IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject const * project, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Finds a property by a given name. /// \param project The project to search. @@ -151,7 +151,7 @@ IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject const * project, /// \param error_out \see errors.h /// \return The property with the given name or null if none was found. IKA_API struct IkarusProperty * -get_property_by_name(IkarusProject const * project, struct IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out); +get_property_by_name_and_scope(IkarusProject * project, struct IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out); /// \brief Finds a blueprint by a given name. /// \param project The project to search. @@ -162,7 +162,7 @@ get_property_by_name(IkarusProject const * project, struct IkarusPropertyScope * /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The blueprint with the given name or null if none was found. -IKA_API struct IkarusBlueprint * get_blueprints_by_name(IkarusProject const * project, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index 326129d..dde1193 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -17,7 +17,7 @@ IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, project, nullptr, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, nullptr, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 94e8b75..0b3da7f 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -12,7 +12,7 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, project, nullptr, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, nullptr, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/properties/util.hpp b/src/ikarus/objects/properties/util.hpp index 092296e..2af0e32 100644 --- a/src/ikarus/objects/properties/util.hpp +++ b/src/ikarus/objects/properties/util.hpp @@ -23,7 +23,7 @@ T * create_property( IKARUS_FAIL_IF_NULL(project, nullptr); IKARUS_FAIL_IF_NULL(property_scope, nullptr); IKARUS_FAIL_IF_PROPERTY_SCOPE_INVALID(property_scope, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, project, property_scope, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, property_scope, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp index 29857cf..d0b9fa2 100644 --- a/src/ikarus/objects/util.hpp +++ b/src/ikarus/objects/util.hpp @@ -96,15 +96,18 @@ name_is_unique(IkarusProject * project, std::string_view name, IkarusProperty co return name_is_unique(project, name, scope.get(), error_out); } -#define IKARUS_FAIL_IF_NAME_INVALID(name, project, object, ret, ...) \ - IKARUS_FAIL_IF_NULL(name, ret); \ - IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); \ - IKARUS_FAIL_IF( \ - !ikarus::util::name_is_unique(project, name, object, error_out), \ - ret, \ - "name must be unique", \ - IkarusErrorInfo_Client_InvalidInput \ - ); \ +#define IKARUS_FAIL_IF_NAME_INVALID(name, ret) \ + IKARUS_FAIL_IF_NULL(name, ret); \ + IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); + +#define IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, object, ret, ...) \ + IKARUS_FAIL_IF_NAME_INVALID(name, ret); \ + IKARUS_FAIL_IF( \ + !ikarus::util::name_is_unique(project, name, object, error_out), \ + ret, \ + "name must be unique", \ + IkarusErrorInfo_Client_InvalidInput \ + ); \ IKARUS_FAIL_IF_ERROR(ret); template @@ -112,7 +115,7 @@ void object_set_name(O * object, char const * name, IkarusErrorData * error_out) IKARUS_FAIL_IF_NULL(object, ); IKARUS_FAIL_IF_OBJECT_MISSING(object, ); IKARUS_FAIL_IF_NULL(name, ); - IKARUS_FAIL_IF_NAME_INVALID(name, object->project, object, ); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, object->project, object, ); IKARUS_TRYRV_OR_FAIL( , diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 6c460f7..b7c3e89 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -275,3 +276,60 @@ size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusErrorData return ret; } + +struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + // TODO, 'InvalidInput' doesn't really make sense here, we'd need to adjust the macros to support distinguishing between different + // errors. In this case `sqlitecpp::MissingRow` and database related errors. Same for the other functions. + IKARUS_VTRYRV_OR_FAIL( + auto const id, + nullptr, + "unable to find entity in database: {}", + IkarusErrorInfo_Client_InvalidInput, + project->db->query_one("SELECT `id` FROM `entities` WHERE `name` = ?", name) + ); + + return project->get_entity(id); +} + +struct IkarusProperty * +get_property_by_name(IkarusProject * project, IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto const id_and_type, + nullptr, + "unable to find property in database: {}", + IkarusErrorInfo_Client_InvalidInput, + project->db->query_one( + "SELECT `id`, `type` FROM `properties` WHERE `name` = ? AND `scope` = ?", + name, + scope->get_id() + ) + ); + + auto const [id, type] = id_and_type; + + return project->get_property(id, type); +} + +IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto const id, + nullptr, + "unable to find blueprint in database: {}", + IkarusErrorInfo_Client_InvalidInput, + project->db->query_one("SELECT `id` FROM `blueprints` WHERE `name` = ?", name) + ); + + return project->get_blueprint(id); +} From 23bb8e84ceb8803058d4a79b134ac82462e85eba Mon Sep 17 00:00:00 2001 From: Folling Date: Tue, 30 Jan 2024 09:46:19 +0100 Subject: [PATCH 132/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index eadf323..3904483 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit eadf3237bfa853763b332777e9dc0f16df8cca71 +Subproject commit 39044837cd3b332aa874325ef36e5df033b092c3 From 56bc471865800895045c15cdc9b4fff3f1f37d18 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 1 Feb 2024 11:15:32 +0100 Subject: [PATCH 133/166] rename project object_by_name_functions with ikarus_project prefix Signed-off-by: Folling --- include/ikarus/persistence/project.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index d0116a4..acd113a 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -135,7 +135,7 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject * project, Ikaru /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The entity with the given name or null if none was found. -IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusEntity * ikarus_project_get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Finds a property by a given name. /// \param project The project to search. @@ -150,8 +150,12 @@ IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject * project, char c /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The property with the given name or null if none was found. -IKA_API struct IkarusProperty * -get_property_by_name_and_scope(IkarusProject * project, struct IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusProperty * ikarus_project_get_property_by_name_and_scope( + IkarusProject * project, + struct IkarusPropertyScope * scope, + char const * name, + IkarusErrorData * error_out +); /// \brief Finds a blueprint by a given name. /// \param project The project to search. @@ -162,7 +166,8 @@ get_property_by_name_and_scope(IkarusProject * project, struct IkarusPropertySco /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The blueprint with the given name or null if none was found. -IKA_API struct IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusBlueprint * +ikarus_project_get_blueprint_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); IKARUS_END_HEADER From aab23b9a48dc32e7ffb9da65b1c0f932839fc5e4 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 1 Feb 2024 19:51:37 +0100 Subject: [PATCH 134/166] unmodernize id.cpp Signed-off-by: Folling --- src/ikarus/id.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ikarus/id.cpp b/src/ikarus/id.cpp index 4269215..9749667 100644 --- a/src/ikarus/id.cpp +++ b/src/ikarus/id.cpp @@ -2,13 +2,13 @@ #include -constexpr uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; -constexpr uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; +uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; +uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; -auto ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) -> IkarusId { +IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) { return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); } -auto ikarus_id_get_object_type(IkarusId id) -> IkarusObjectType { +IkarusObjectType ikarus_id_get_object_type(IkarusId id) { return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); } From 6996578ec1ed5a08d9519b7746cc20e53a3f4b96 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 10 Feb 2024 15:44:20 +0100 Subject: [PATCH 135/166] fixup cppbase::visit overloaded ctor usage Signed-off-by: Folling --- src/ikarus/objects/properties/property_scope.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ikarus/objects/properties/property_scope.cpp b/src/ikarus/objects/properties/property_scope.cpp index a0b9ade..b8ec32f 100644 --- a/src/ikarus/objects/properties/property_scope.cpp +++ b/src/ikarus/objects/properties/property_scope.cpp @@ -48,10 +48,10 @@ void ikarus_property_source_visit_const( void * user_data ) { std::visit( - cppbase::overloaded( + cppbase::overloaded{ [blueprint_visitor, user_data](IkarusBlueprint const * blueprint) { blueprint_visitor(blueprint, user_data); }, [entity_visitor, user_data](IkarusEntity const * entity) { entity_visitor(entity, user_data); } - ), + }, property_source->data ); } From 502cedf904d70f3a021176d0ebcf95d784b19b86 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 10 Feb 2024 15:55:11 +0100 Subject: [PATCH 136/166] fixup Boost_INCLUDE_DIRS -= S Signed-off-by: Folling --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01e5f44..8fe8e89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries( target_include_directories( ikarus PRIVATE - ${Boost_INCLUDE_DIRS} + ${Boost_INCLUDE_DIR} ) if (LIBIKARUS_ENABLE_LINTS) @@ -91,4 +91,4 @@ if (LIBIKARUS_BUILD_DOCS) libikarus_tests libikarus_docs ) -endif () \ No newline at end of file +endif () From e18a0c42b32291adfda2a93afbb122e0d9e5d8a6 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 11 Feb 2024 15:23:18 +0100 Subject: [PATCH 137/166] fixup constness of IkarusProject in ikarus_project_get_entity_count Signed-off-by: Folling --- include/ikarus/persistence/project.h | 2 +- src/ikarus/persistence/project.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index acd113a..18a35f5 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -124,7 +124,7 @@ IKA_API void ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); /// \brief Finds an entity by a given name. /// \param project The project to search. diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index b7c3e89..9c264f1 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -263,7 +263,7 @@ void ikarus_project_get_entities( } } -size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusErrorData * error_out) { +size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, 0); IKARUS_VTRYRV_OR_FAIL( From 3e87899da72cd634fcd1b41928566d9f6fcc2b44 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 11 Feb 2024 16:39:07 +0100 Subject: [PATCH 138/166] fixup inclusion of header in entity.cpp Signed-off-by: Folling --- src/ikarus/objects/entity.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 0b3da7f..9e791a7 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -1,9 +1,10 @@ -#include "entity.hpp" +#include "ikarus/objects/entity.h" #include #include #include +#include #include #include #include From 31e8432ccf55d592d379992a09d6dd7f62c6675e Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 10:44:05 +0100 Subject: [PATCH 139/166] add module.modulemap Signed-off-by: Folling --- include/module.modulemap | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 include/module.modulemap diff --git a/include/module.modulemap b/include/module.modulemap new file mode 100644 index 0000000..6b2dbcb --- /dev/null +++ b/include/module.modulemap @@ -0,0 +1,6 @@ +module ikarus { + header "ikarus/persistence/project.h" + header "ikarus/objects/entity.h" + header "ikarus/objects/blueprint.h" + export * +} \ No newline at end of file From 9299c85d6562a1b8e148248bbe15df4ecdc12482 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 12:06:22 +0100 Subject: [PATCH 140/166] expose id getters Signed-off-by: Folling --- include/ikarus/objects/blueprint.h | 9 +++++++++ include/ikarus/objects/entity.h | 9 +++++++++ include/ikarus/objects/properties/property.h | 17 +++++++++++++++++ src/ikarus/objects/blueprint.cpp | 4 ++++ src/ikarus/objects/entity.cpp | 4 ++++ src/ikarus/objects/properties/property.cpp | 4 ++++ src/ikarus/objects/util.hpp | 8 ++++++++ 7 files changed, 55 insertions(+) diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index adfacd4..ec2087f 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -4,6 +4,7 @@ /// \author Folling #include +#include #include #include @@ -38,6 +39,14 @@ IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project /// \remark The blueprint must not be accessed after deletion. IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * error_out); +/// \brief Gets the ID of a blueprint. +/// \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 IkarusId 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. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 53abc70..04d59b0 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -54,6 +55,14 @@ IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char /// \remark The entity must not be accessed after deletion. IKA_API void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); +/// \brief Gets the ID of an entity. +/// \param entity The entity 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 entity or 0 if an error occurs. +IKA_API IkarusId ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out); + /// \brief Gets the project an entity is part of. /// \param entity The entity to get the project of. /// \pre \li Must not be null. diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index 589854f..aa52b2c 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -4,6 +4,7 @@ /// \author Folling #include +#include #include #include #include @@ -64,6 +65,22 @@ struct IkarusProperty; /// \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 IkarusId 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 type info of a property. /// \param property The property to get the type info of. /// \pre \li Must not be null. diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index dde1193..aa5f35c 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -49,6 +49,10 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * erro blueprint->project->uncache(blueprint); } +IkarusId 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); } diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 9e791a7..d724259 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -45,6 +45,10 @@ void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { entity->project->uncache(entity); } +IkarusId 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); } diff --git a/src/ikarus/objects/properties/property.cpp b/src/ikarus/objects/properties/property.cpp index f45adbb..929d3a6 100644 --- a/src/ikarus/objects/properties/property.cpp +++ b/src/ikarus/objects/properties/property.cpp @@ -28,6 +28,10 @@ IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * property->project->uncache(property); } +IkarusId 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); } diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp index d0b9fa2..8e037ca 100644 --- a/src/ikarus/objects/util.hpp +++ b/src/ikarus/objects/util.hpp @@ -16,6 +16,14 @@ namespace ikarus::util { +template +[[nodiscard]] IkarusId 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 [[nodiscard]] IkarusProject * object_get_project(O const * object, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(object, nullptr); From 48eb35161e3494269cd6cd52ff4bdf99d0e75829 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 15:21:25 +0100 Subject: [PATCH 141/166] fixup missing ikarus_ prefix for error_info_name Signed-off-by: Folling --- include/ikarus/errors.h | 2 +- src/ikarus/errors.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index ce11788..a7a4fb4 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -107,7 +107,7 @@ struct IkarusErrorData { /// \param info The error info to get the name of. /// \return The name of the error info. /// \remark The returned pointer is valid for the lifetime of the program and must not be freed. -IKA_API char const * get_error_info_name(IkarusErrorInfo info); +IKA_API char const * ikarus_get_error_info_name(IkarusErrorInfo info); /// \brief Checks if an error data is a success. /// \param data The error data to check. diff --git a/src/ikarus/errors.cpp b/src/ikarus/errors.cpp index d98b28c..35014f8 100644 --- a/src/ikarus/errors.cpp +++ b/src/ikarus/errors.cpp @@ -8,7 +8,7 @@ #include -char const * get_error_info_name(IkarusErrorInfo info) { +char const * ikarus_get_error_info_name(IkarusErrorInfo info) { switch (info) { case IkarusErrorInfo_None: return "None"; From 3ab071403d6f46cb61fb9fdb8f6ec3b0122cb5c5 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 15:36:44 +0100 Subject: [PATCH 142/166] invert condition for whether name is unique Signed-off-by: Folling --- src/ikarus/objects/util.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp index 8e037ca..4b55caa 100644 --- a/src/ikarus/objects/util.hpp +++ b/src/ikarus/objects/util.hpp @@ -63,7 +63,7 @@ template project->db->query_one("SELECT EXISTS(SELECT 1 FROM `blueprints` WHERE `name` = ?)", name) ); - return exists; + return !exists; } [[nodiscard]] inline bool name_is_unique( @@ -80,7 +80,7 @@ template project->db->query_one("SELECT EXISTS(SELECT 1 FROM `entities` WHERE `name` = ?)", name) ); - return exists; + return !exists; } [[nodiscard]] inline bool @@ -93,7 +93,7 @@ name_is_unique(IkarusProject const * project, std::string_view name, IkarusPrope project->db->query_one("SELECT EXISTS(SELECT 1 FROM `properties` WHERE `name` = ? AND `scope` = ?)", name, scope->get_id()) ); - return exists; + return !exists; } [[nodiscard]] inline bool From 40960db0443e41bebb243acadeeb2ffa7ed5e42d Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 15:49:26 +0100 Subject: [PATCH 143/166] fixup name-duplication checking Signed-off-by: Folling --- src/ikarus/objects/blueprint.cpp | 2 +- src/ikarus/objects/entity.cpp | 2 +- src/ikarus/objects/util.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index aa5f35c..77ffb70 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -17,7 +17,7 @@ IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, nullptr, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index d724259..bd5aa25 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -13,7 +13,7 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, nullptr, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp index 4b55caa..1de035a 100644 --- a/src/ikarus/objects/util.hpp +++ b/src/ikarus/objects/util.hpp @@ -97,7 +97,7 @@ name_is_unique(IkarusProject const * project, std::string_view name, IkarusPrope } [[nodiscard]] inline bool -name_is_unique(IkarusProject * project, std::string_view name, IkarusProperty const * property, IkarusErrorData * error_out) { +name_is_unique(IkarusProject const * project, std::string_view name, IkarusProperty const * property, IkarusErrorData * error_out) { std::unique_ptr scope{ikarus_property_get_scope(property, error_out)}; IKARUS_FAIL_IF_ERROR(false); From 7499d1d38e8aa700b690f38dd3970cb1588aa240 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 15:52:08 +0100 Subject: [PATCH 144/166] fixup object creation queries Signed-off-by: Folling --- src/ikarus/objects/blueprint.cpp | 4 ++-- src/ikarus/objects/entity.cpp | 6 +++--- src/ikarus/objects/properties/util.hpp | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index 77ffb70..a1cf9e5 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -25,9 +25,9 @@ IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char c "failed to create blueprint: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Blueprint, name, "")); + TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?)", IkarusObjectType_Blueprint)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id)); + TRY(db->execute("INSERT INTO `blueprints`(`id`, `name`) VALUES(?, ?)", id, name)); return cppbase::ok(id); }) ); diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index bd5aa25..150b91f 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -13,7 +13,7 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, @@ -21,9 +21,9 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * "failed to create entity: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?, ?, ?)", IkarusObjectType_Entity)); + TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?)", IkarusObjectType_Entity)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Entity); - TRY(db->execute("INSERT INTO `entities`(`id`, `name`) VALUES(?)", id, name)); + TRY(db->execute("INSERT INTO `entities`(`id`, `name`) VALUES(?, ?)", id, name)); return cppbase::ok(id); }) ); diff --git a/src/ikarus/objects/properties/util.hpp b/src/ikarus/objects/properties/util.hpp index 2af0e32..c095a18 100644 --- a/src/ikarus/objects/properties/util.hpp +++ b/src/ikarus/objects/properties/util.hpp @@ -31,11 +31,12 @@ T * create_property( "failed to create property: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name, property_scope](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`, `name`, `information`) VALUES(?, ?, ?)", IkarusObjectType_Property, name, "")); + 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`, `type`, `source`) VALUES(?, ?, ?)", + "INSERT INTO `properties`(`id`, `name`, `type`, `source`) VALUES(?, ?, ?, ?)", id, + name, T::PropertyType, property_scope->get_id() )); From 38b0a86fd76c9b70f3467a0b06aa0f21f13a24cb Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 16:02:51 +0100 Subject: [PATCH 145/166] fixup entity deletion query Signed-off-by: Folling --- src/ikarus/objects/entity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 150b91f..e1dbf5a 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -39,7 +39,7 @@ void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { , "unable to delete entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->execute("DELETE FROM `objects` WHERE `id` == ?", entity->id) + entity->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", entity->id) ); entity->project->uncache(entity); From b96792e8c45beaf4cbf560da6d0dedcf951922ea Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 16:10:41 +0100 Subject: [PATCH 146/166] print error message when unable to check for project path existence Signed-off-by: Folling --- src/ikarus/persistence/project.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 9c264f1..b2e92f8 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -70,7 +70,12 @@ IkarusProject * ikarus_project_create(char const * path, char const * name, Ikar boost::system::error_code ec; bool const exists = fs::exists(fs_path, ec); - IKARUS_FAIL_IF(ec, nullptr, "unable to check whether path is occupied", IkarusErrorInfo_Filesystem_AlreadyExists); + IKARUS_FAIL_IF( + ec, + 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); } From 0474fce07ba82138ab330b6920444ee25857e27a Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 16:23:01 +0100 Subject: [PATCH 147/166] filter ENOENT from boost::error_code check Signed-off-by: Folling --- src/ikarus/persistence/project.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index b2e92f8..87ebef5 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -69,9 +69,8 @@ IkarusProject * ikarus_project_create(char const * path, char const * name, Ikar { boost::system::error_code ec; bool const exists = fs::exists(fs_path, ec); - IKARUS_FAIL_IF( - ec, + 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 From ff0eda005dec5623ffae3bff97264fcf087c3643 Mon Sep 17 00:00:00 2001 From: Folling Date: Mon, 12 Feb 2024 16:39:44 +0100 Subject: [PATCH 148/166] update sqlitecpp Signed-off-by: Folling --- src/ikarus/persistence/migrations/m0_initial_layout.sql | 2 +- vendor/sqlitecpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index 0031cee..9bf3cc7 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -48,7 +48,7 @@ CREATE TABLE `properties` `source` INT NOT NULL, PRIMARY KEY (`id`), - UNIQUE(`source`, `name`), + UNIQUE (`source`, `name`), FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE, FOREIGN KEY (`source`) REFERENCES `objects` (`id`) ON DELETE CASCADE ) WITHOUT ROWID, STRICT; diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 3904483..7832ea6 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 39044837cd3b332aa874325ef36e5df033b092c3 +Subproject commit 7832ea68dca4c8367dd938b079a01ccffe6a7acd From aec7e62545b48b7c027583f4c06b6a2c2e1685ee Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 15 Feb 2024 22:04:37 +0100 Subject: [PATCH 149/166] improve cmake setup Signed-off-by: Folling --- .gitmodules | 3 +++ CMakeLists.txt | 29 +++++++---------------------- vendor/CMakeLists.txt | 3 ++- vendor/catch2 | 1 - vendor/cppbase | 1 + vendor/sqlitecpp | 2 +- 6 files changed, 14 insertions(+), 25 deletions(-) delete mode 160000 vendor/catch2 create mode 160000 vendor/cppbase diff --git a/.gitmodules b/.gitmodules index a26cc62..933638b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "vendor/doxygen-awesome-css"] path = vendor/doxygen-awesome-css url = git@github.com:jothepro/doxygen-awesome-css.git +[submodule "vendor/cppbase"] + path = vendor/cppbase + url = ssh://git@git.rewritesarebliss.com:16658/Folling/cppbase.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fe8e89..92e04cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,13 +5,6 @@ option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF) option(LIBIKARUS_ENABLE_LINTS "Enable linting" OFF) option(LIBIKARUS_BUILD_DOCS "Build documentation" OFF) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_STANDARD 23) - -set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) - -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - add_subdirectory(vendor) add_subdirectory(include) add_subdirectory(src) @@ -24,6 +17,13 @@ add_library( ${SOURCE_FILES} ) +set_target_properties( + ikarus PROPERTIES + CXX_STANDARD 23 + CXX_STANDARD_REQUIRED ON + POSITION_INDEPENDENT_CODE TRUE +) + target_include_directories( ikarus PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include @@ -57,21 +57,6 @@ if (LIBIKARUS_ENABLE_LINTS) ) endif () -if (LIBIKARUS_ENABLE_TESTS) - add_executable(libikarus_tests ${SOURCE_FILES}) - target_link_libraries(libikarus_tests PRIVATE sqlitecpp Catch2::Catch2WithMain) - - target_include_directories( - libikarus_tests PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/include - ${CMAKE_CURRENT_LIST_DIR}/src - ) - - include(CTest) - include(vendor/catch2/extras/Catch.cmake) - catch_discover_tests(libikarus_tests) -endif () - if (LIBIKARUS_BUILD_DOCS) find_program(DOXYGEN_PATH NAMES doxygen REQUIRED) add_custom_target( diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 9820477..611db2e 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -1,2 +1,3 @@ -add_subdirectory(catch2) +# order is important here since sqlitecpp otherwise duplicates cppbase +add_subdirectory(cppbase) add_subdirectory(sqlitecpp) diff --git a/vendor/catch2 b/vendor/catch2 deleted file mode 160000 index 5bba3e4..0000000 --- a/vendor/catch2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5bba3e4038602badb691da914523f667a2dd1f27 diff --git a/vendor/cppbase b/vendor/cppbase new file mode 160000 index 0000000..6bf80cf --- /dev/null +++ b/vendor/cppbase @@ -0,0 +1 @@ +Subproject commit 6bf80cf9d5ff54ab300f7e88a8cb9996f441dae6 diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 7832ea6..4a97c47 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 7832ea68dca4c8367dd938b079a01ccffe6a7acd +Subproject commit 4a97c47847cbe9cf610a636137f678b447cdde5a From b5852698e35275b51b9107ce7811c5aa75e9d498 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 15 Feb 2024 23:30:54 +0100 Subject: [PATCH 150/166] update sqlitecpp Signed-off-by: Folling --- vendor/sqlitecpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 4a97c47..32fa3c1 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 4a97c47847cbe9cf610a636137f678b447cdde5a +Subproject commit 32fa3c1a9beae1a2c49fdd08cc659ea6153da666 From 70820129aeac36a3045ec78c5a7afa2c17ded13f Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 12 May 2024 14:15:42 +0200 Subject: [PATCH 151/166] add flatbuffers support and initial rewrite Signed-off-by: Folling --- .clang-format | 6 +- CMakeLists.txt | 10 +- include/CMakeLists.txt | 1 + include/ikarus/CMakeLists.txt | 1 + include/ikarus/errors.h | 5 +- include/ikarus/global.h | 20 - include/ikarus/id.h | 46 - include/ikarus/macros.h | 34 +- include/ikarus/models/CMakeLists.txt | 41 + include/ikarus/models/blueprint.fbs | 12 + include/ikarus/models/blueprint_generated.h | 153 ++ include/ikarus/models/entity.fbs | 26 + include/ikarus/models/entity_generated.h | 398 ++++ include/ikarus/models/property.fbs | 14 + include/ikarus/models/property_generated.h | 289 +++ include/ikarus/models/value.fbs | 85 + include/ikarus/models/value_generated.h | 1719 +++++++++++++++++ include/ikarus/objects/blueprint.h | 60 +- include/ikarus/objects/entity.h | 205 +- include/ikarus/objects/object.h | 50 - include/ikarus/objects/object_type.h | 34 - .../objects/properties/number_property.h | 26 - include/ikarus/objects/properties/property.h | 118 +- .../objects/properties/property_scope.h | 62 - .../ikarus/objects/properties/property_type.h | 27 - .../ikarus/objects/properties/text_property.h | 27 +- .../objects/properties/toggle_property.h | 31 +- include/ikarus/persistence/project.h | 77 +- include/ikarus/values/entity_property_value.h | 46 - include/ikarus/values/number_value.h | 54 +- include/ikarus/values/text_value.h | 69 +- include/ikarus/values/toggle_value.h | 63 +- include/ikarus/values/value.h | 53 +- include/ikarus/values/value_cardinality.h | 23 + include/ikarus/values/value_type.h | 198 ++ src/ikarus/errors.cpp | 8 - src/ikarus/errors.hpp | 13 - src/ikarus/global.cpp | 7 - src/ikarus/id.cpp | 14 - src/ikarus/objects/blueprint.cpp | 38 +- src/ikarus/objects/blueprint.hpp | 8 +- src/ikarus/objects/entity.cpp | 307 ++- src/ikarus/objects/entity.hpp | 9 +- src/ikarus/objects/object.cpp | 88 +- src/ikarus/objects/object.hpp | 21 +- src/ikarus/objects/object_type.cpp | 12 - .../objects/properties/number_property.cpp | 2 +- .../objects/properties/number_property.hpp | 8 +- src/ikarus/objects/properties/property.cpp | 20 +- src/ikarus/objects/properties/property.hpp | 4 +- .../objects/properties/property_scope.cpp | 57 - .../objects/properties/property_scope.hpp | 39 - .../objects/properties/text_property.cpp | 2 +- .../objects/properties/text_property.hpp | 8 +- .../objects/properties/toggle_property.cpp | 2 +- .../objects/properties/toggle_property.hpp | 11 +- src/ikarus/objects/properties/util.hpp | 7 +- src/ikarus/objects/util.hpp | 79 +- src/ikarus/persistence/migrations.hpp | 3 +- .../migrations/m0_initial_layout.sql | 87 +- src/ikarus/persistence/project.cpp | 26 +- src/ikarus/persistence/project.hpp | 21 +- src/ikarus/values/entity_property_value.hpp | 6 - src/ikarus/values/number_value.cpp | 56 +- src/ikarus/values/number_value.hpp | 2 +- src/ikarus/values/text_value.cpp | 56 +- src/ikarus/values/text_value.hpp | 2 +- src/ikarus/values/toggle_value.cpp | 56 +- src/ikarus/values/toggle_value.hpp | 2 +- src/ikarus/values/value.cpp | 78 +- src/ikarus/values/value.hpp | 28 +- src/ikarus/values/value_base.hpp | 62 +- 72 files changed, 3929 insertions(+), 1403 deletions(-) create mode 100644 include/ikarus/CMakeLists.txt delete mode 100644 include/ikarus/global.h delete mode 100644 include/ikarus/id.h create mode 100644 include/ikarus/models/CMakeLists.txt create mode 100644 include/ikarus/models/blueprint.fbs create mode 100644 include/ikarus/models/blueprint_generated.h create mode 100644 include/ikarus/models/entity.fbs create mode 100644 include/ikarus/models/entity_generated.h create mode 100644 include/ikarus/models/property.fbs create mode 100644 include/ikarus/models/property_generated.h create mode 100644 include/ikarus/models/value.fbs create mode 100644 include/ikarus/models/value_generated.h delete mode 100644 include/ikarus/objects/object.h delete mode 100644 include/ikarus/objects/object_type.h delete mode 100644 include/ikarus/objects/properties/property_scope.h delete mode 100644 include/ikarus/objects/properties/property_type.h delete mode 100644 include/ikarus/values/entity_property_value.h create mode 100644 include/ikarus/values/value_cardinality.h create mode 100644 include/ikarus/values/value_type.h delete mode 100644 src/ikarus/global.cpp delete mode 100644 src/ikarus/id.cpp delete mode 100644 src/ikarus/objects/object_type.cpp delete mode 100644 src/ikarus/objects/properties/property_scope.cpp delete mode 100644 src/ikarus/objects/properties/property_scope.hpp diff --git a/.clang-format b/.clang-format index 7d8a46d..85337cc 100644 --- a/.clang-format +++ b/.clang-format @@ -64,9 +64,9 @@ BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon -BreakStringLiterals: false +BreakStringLiterals: true -ColumnLimit: 140 +ColumnLimit: 80 CommentPragmas: '^\\.+' CompactNamespaces: false ConstructorInitializerIndentWidth: 4 @@ -117,7 +117,7 @@ IndentCaseBlocks: false IndentCaseLabels: false IndentExternBlock: NoIndent IndentGotoLabels: false -IndentPPDirectives: None +IndentPPDirectives: BeforeHash IndentRequiresClause: true IndentWidth: 4 IndentWrappedFunctionNames: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 92e04cc..8a16b08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,11 @@ add_library( ${SOURCE_FILES} ) +add_dependencies( + ikarus + flatbuffer_headers +) + set_target_properties( ikarus PROPERTIES CXX_STANDARD 23 @@ -71,9 +76,4 @@ if (LIBIKARUS_BUILD_DOCS) ikarus libikarus_docs ) - - add_dependencies( - libikarus_tests - libikarus_docs - ) endif () diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 2e91ee9..e3043c7 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -6,3 +6,4 @@ file( set(INCLUDE_FILES ${FILES} PARENT_SCOPE) +add_subdirectory(ikarus) \ No newline at end of file diff --git a/include/ikarus/CMakeLists.txt b/include/ikarus/CMakeLists.txt new file mode 100644 index 0000000..cd981b8 --- /dev/null +++ b/include/ikarus/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(models) \ No newline at end of file diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index a7a4fb4..926f07d 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -4,11 +4,10 @@ /// \author Folling #include -#include /// \addtogroup errors Errors /// \brief Error handling within libikarus -/// \details Functions in Ikarus may fail, in which case they 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. /// If the out parameter is null nothing will be stored. This is not recommended as it essentially ignores errors. /// For the sake of simplicity we have avoided mechanisms that "force" clients to handle errors. @@ -93,7 +92,7 @@ enum IkarusErrorInfo { }; /// \brief The maximum length of an error message. -size_t const IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT = 128; +#define IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT 128 /// \brief The data stored for an error struct IkarusErrorData { diff --git a/include/ikarus/global.h b/include/ikarus/global.h deleted file mode 100644 index 4665863..0000000 --- a/include/ikarus/global.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -/// \file global.h -/// \author Folling - -#include - -/// \defgroup global Global -/// \brief Information relevant to the entire library. -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief Frees a pointer allocated by ikarus. Every pointer returned by a function must be freed using this function -/// unless explicitly stated otherwise. -IKA_API void ikarus_free(void * ptr); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/id.h b/include/ikarus/id.h deleted file mode 100644 index c76e341..0000000 --- a/include/ikarus/id.h +++ /dev/null @@ -1,46 +0,0 @@ -// IMPLEMENTATION_DETAIL_DATABASE - -/// \file id.h -/// \author Folling - -/// \privatesection - -#pragma once - -#include -#include -#include - -/// \defgroup id Ids -/// \brief Ids are used to identify objects in the database. -/// \details They are stored as 64 bit integers with the following layout: -/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. -/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. -/// - next 7 bits: #IkarusObjectType -/// - last 56 bits: incremented counter generated by the database -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief A wrapper around a 64 bit integer that represents the id of an object. -/// \details They are stored as 64 bit integers with the following layout: -/// - first bit: ignored, technically we could use it, but SQLite doesn't support u64 integers. -/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit. -/// - next 7 bits: #IkarusObjectType -/// - last 56 bits: incremented counter generated by the database -typedef int64_t IkarusId; - -/// \brief Creates an id from the given data and type. -/// \param data The data to use for the id. -/// \param type The type to use for the id. -/// \return The created id. -IKA_API IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type); - -/// \brief Fetches the object type of the given id. -/// \param id The id to fetch the object type for. -/// \return The object type of the given id. -IKA_API IkarusObjectType ikarus_id_get_object_type(IkarusId id); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/macros.h b/include/ikarus/macros.h index fdc6510..e7714dc 100644 --- a/include/ikarus/macros.h +++ b/include/ikarus/macros.h @@ -1,27 +1,23 @@ #pragma once -#if defined(__unix__) - -#define IKA_OS_FAMILY_UNIX -#define IKA_API __attribute__((visibility("default"))) - -#if defined(linux) -#define IKA_OS_LINUX -#endif - -#elif defined(_WIN32) || defined(WIN32) -#define IKA_OS_WIN -#define IKA_API __declspec(dllexport) -#endif - -#ifndef IKA_API #define IKA_API + +#if defined(__unix__) + #define IKA_OS_FAMILY_UNIX + #define IKA_API __attribute__((visibility("default"))) + + #if defined(linux) + #define IKA_OS_LINUX + #endif +#elif defined(_WIN32) + #define IKA_OS_WIN + #define IKA_API __declspec(dllexport) #endif #ifdef __cplusplus -#define IKARUS_BEGIN_HEADER extern "C" { -#define IKARUS_END_HEADER } + #define IKARUS_BEGIN_HEADER extern "C" { + #define IKARUS_END_HEADER } #else -#define IKARUS_BEGIN_HEADER -#define IKARUS_END_HEADER + #define IKARUS_BEGIN_HEADER + #define IKARUS_END_HEADER #endif diff --git a/include/ikarus/models/CMakeLists.txt b/include/ikarus/models/CMakeLists.txt new file mode 100644 index 0000000..e3d8758 --- /dev/null +++ b/include/ikarus/models/CMakeLists.txt @@ -0,0 +1,41 @@ +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 +) diff --git a/include/ikarus/models/blueprint.fbs b/include/ikarus/models/blueprint.fbs new file mode 100644 index 0000000..fb782d8 --- /dev/null +++ b/include/ikarus/models/blueprint.fbs @@ -0,0 +1,12 @@ +include "property.fbs"; + +namespace Ikarus; + +table Blueprint { + id: int64; + name: string; + description: string; + tags: [string]; +} + +root_type Blueprint; diff --git a/include/ikarus/models/blueprint_generated.h b/include/ikarus/models/blueprint_generated.h new file mode 100644 index 0000000..b70924b --- /dev/null +++ b/include/ikarus/models/blueprint_generated.h @@ -0,0 +1,153 @@ +// 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(VT_ID, 0); + } + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + const ::flatbuffers::String *description() const { + return GetPointer(VT_DESCRIPTION); + } + const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { + return GetPointer> *>(VT_TAGS); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(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(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 Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset 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 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(buf); +} + +inline const Ikarus::Blueprint *GetSizePrefixedBlueprint(const void *buf) { + return ::flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyBlueprintBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedBlueprintBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishBlueprintBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedBlueprintBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace Ikarus + +#endif // FLATBUFFERS_GENERATED_BLUEPRINT_IKARUS_H_ diff --git a/include/ikarus/models/entity.fbs b/include/ikarus/models/entity.fbs new file mode 100644 index 0000000..bce2ff5 --- /dev/null +++ b/include/ikarus/models/entity.fbs @@ -0,0 +1,26 @@ +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; diff --git a/include/ikarus/models/entity_generated.h b/include/ikarus/models/entity_generated.h new file mode 100644 index 0000000..e253038 --- /dev/null +++ b/include/ikarus/models/entity_generated.h @@ -0,0 +1,398 @@ +// 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(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 + 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(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 value) { + fbb_.AddOffset(NamedValue::VT_VALUE, value); + } + explicit NamedValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + fbb_.Required(o, NamedValue::VT_NAME); + return o; + } +}; + +inline ::flatbuffers::Offset CreateNamedValue( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, + ::flatbuffers::Offset 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 CreateNamedValueDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + ::flatbuffers::Offset 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(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(property_id() > _property_id) - static_cast(property_id() < _property_id); + } + Ikarus::Value::Data data_type() const { + return static_cast(GetField(VT_DATA_TYPE, 0)); + } + const void *data() const { + return GetPointer(VT_DATA); + } + template const T *data_as() const; + const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { + return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { + return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { + return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::SimpleData *data_as_SimpleData() const { + return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::CombinedData *data_as_CombinedData() const { + return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ListData *data_as_ListData() const { + return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ComplexData *data_as_ComplexData() const { + return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_PROPERTY_ID, 8) && + VerifyField(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() const { + return data_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *PropertyValue::data_as() const { + return data_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *PropertyValue::data_as() const { + return data_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *PropertyValue::data_as() const { + return data_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *PropertyValue::data_as() const { + return data_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *PropertyValue::data_as() const { + return data_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *PropertyValue::data_as() 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(PropertyValue::VT_PROPERTY_ID, property_id, 0); + } + void add_data_type(Ikarus::Value::Data data_type) { + fbb_.AddElement(PropertyValue::VT_DATA_TYPE, static_cast(data_type), 0); + } + void add_data(::flatbuffers::Offset data) { + fbb_.AddOffset(PropertyValue::VT_DATA, data); + } + explicit PropertyValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreatePropertyValue( + ::flatbuffers::FlatBufferBuilder &_fbb, + int64_t property_id = 0, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset 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(VT_ID, 0); + } + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + const ::flatbuffers::String *description() const { + return GetPointer(VT_DESCRIPTION); + } + const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { + return GetPointer> *>(VT_TAGS); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *values() const { + return GetPointer> *>(VT_VALUES); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *property_values() const { + return GetPointer> *>(VT_PROPERTY_VALUES); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(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(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>> values) { + fbb_.AddOffset(Entity::VT_VALUES, values); + } + void add_property_values(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> property_values) { + fbb_.AddOffset(Entity::VT_PROPERTY_VALUES, property_values); + } + explicit EntityBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset 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>> values = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> 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 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> *values = nullptr, + std::vector<::flatbuffers::Offset> *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(values) : 0; + auto property_values__ = property_values ? _fbb.CreateVectorOfSortedTables(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(buf); +} + +inline const Ikarus::Entity *GetSizePrefixedEntity(const void *buf) { + return ::flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyEntityBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedEntityBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishEntityBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedEntityBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace Ikarus + +#endif // FLATBUFFERS_GENERATED_ENTITY_IKARUS_H_ diff --git a/include/ikarus/models/property.fbs b/include/ikarus/models/property.fbs new file mode 100644 index 0000000..8a96bb2 --- /dev/null +++ b/include/ikarus/models/property.fbs @@ -0,0 +1,14 @@ +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; diff --git a/include/ikarus/models/property_generated.h b/include/ikarus/models/property_generated.h new file mode 100644 index 0000000..1111bee --- /dev/null +++ b/include/ikarus/models/property_generated.h @@ -0,0 +1,289 @@ +// 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(VT_ID, 0); + } + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + const ::flatbuffers::String *description() const { + return GetPointer(VT_DESCRIPTION); + } + const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { + return GetPointer> *>(VT_TAGS); + } + Ikarus::Value::Schema value_schema_type() const { + return static_cast(GetField(VT_VALUE_SCHEMA_TYPE, 0)); + } + const void *value_schema() const { + return GetPointer(VT_VALUE_SCHEMA); + } + template 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(value_schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *value_schema_as_SimpleSchema() const { + return value_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(value_schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *value_schema_as_CombinedSchema() const { + return value_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(value_schema()) : nullptr; + } + const Ikarus::Value::ListSchema *value_schema_as_ListSchema() const { + return value_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(value_schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *value_schema_as_ComplexSchema() const { + return value_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(value_schema()) : nullptr; + } + Ikarus::Value::Data default_value_type() const { + return static_cast(GetField(VT_DEFAULT_VALUE_TYPE, 0)); + } + const void *default_value() const { + return GetPointer(VT_DEFAULT_VALUE); + } + template 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(default_value()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *default_value_as_NumberDataPoint() const { + return default_value_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(default_value()) : nullptr; + } + const Ikarus::Value::TextDataPoint *default_value_as_TextDataPoint() const { + return default_value_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(default_value()) : nullptr; + } + const Ikarus::Value::SimpleData *default_value_as_SimpleData() const { + return default_value_type() == Ikarus::Value::Data::SimpleData ? static_cast(default_value()) : nullptr; + } + const Ikarus::Value::CombinedData *default_value_as_CombinedData() const { + return default_value_type() == Ikarus::Value::Data::CombinedData ? static_cast(default_value()) : nullptr; + } + const Ikarus::Value::ListData *default_value_as_ListData() const { + return default_value_type() == Ikarus::Value::Data::ListData ? static_cast(default_value()) : nullptr; + } + const Ikarus::Value::ComplexData *default_value_as_ComplexData() const { + return default_value_type() == Ikarus::Value::Data::ComplexData ? static_cast(default_value()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(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(verifier, VT_VALUE_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_VALUE_SCHEMA) && + VerifySchema(verifier, value_schema(), value_schema_type()) && + VerifyField(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() const { + return value_schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *Property::value_schema_as() const { + return value_schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *Property::value_schema_as() const { + return value_schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *Property::value_schema_as() const { + return value_schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *Property::value_schema_as() const { + return value_schema_as_ComplexSchema(); +} + +template<> inline const Ikarus::Value::ToggleDataPoint *Property::default_value_as() const { + return default_value_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *Property::default_value_as() const { + return default_value_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *Property::default_value_as() const { + return default_value_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *Property::default_value_as() const { + return default_value_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *Property::default_value_as() const { + return default_value_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *Property::default_value_as() const { + return default_value_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *Property::default_value_as() 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(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(Property::VT_VALUE_SCHEMA_TYPE, static_cast(value_schema_type), 0); + } + void add_value_schema(::flatbuffers::Offset value_schema) { + fbb_.AddOffset(Property::VT_VALUE_SCHEMA, value_schema); + } + void add_default_value_type(Ikarus::Value::Data default_value_type) { + fbb_.AddElement(Property::VT_DEFAULT_VALUE_TYPE, static_cast(default_value_type), 0); + } + void add_default_value(::flatbuffers::Offset default_value) { + fbb_.AddOffset(Property::VT_DEFAULT_VALUE, default_value); + } + explicit PropertyBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset 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 value_schema = 0, + Ikarus::Value::Data default_value_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset 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 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 value_schema = 0, + Ikarus::Value::Data default_value_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset 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(buf); +} + +inline const Ikarus::Property *GetSizePrefixedProperty(const void *buf) { + return ::flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyPropertyBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedPropertyBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishPropertyBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedPropertyBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace Ikarus + +#endif // FLATBUFFERS_GENERATED_PROPERTY_IKARUS_H_ diff --git a/include/ikarus/models/value.fbs b/include/ikarus/models/value.fbs new file mode 100644 index 0000000..27a27e9 --- /dev/null +++ b/include/ikarus/models/value.fbs @@ -0,0 +1,85 @@ +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; diff --git a/include/ikarus/models/value_generated.h b/include/ikarus/models/value_generated.h new file mode 100644 index 0000000..c9da9e3 --- /dev/null +++ b/include/ikarus/models/value_generated.h @@ -0,0 +1,1719 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_H_ +#define FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_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"); + +namespace Ikarus { +namespace Value { + +struct ToggleDataPoint; +struct ToggleDataPointBuilder; + +struct NumberDataPoint; +struct NumberDataPointBuilder; + +struct TextDataPoint; +struct TextDataPointBuilder; + +struct ConstantSchema; +struct ConstantSchemaBuilder; + +struct SimpleSchema; +struct SimpleSchemaBuilder; + +struct SimpleData; +struct SimpleDataBuilder; + +struct CombinedSchema; +struct CombinedSchemaBuilder; + +struct CombinedData; +struct CombinedDataBuilder; + +struct ListSchema; +struct ListSchemaBuilder; + +struct ListData; +struct ListDataBuilder; + +struct NamedSchema; +struct NamedSchemaBuilder; + +struct ComplexSchema; +struct ComplexSchemaBuilder; + +struct NamedData; +struct NamedDataBuilder; + +struct ComplexData; +struct ComplexDataBuilder; + +struct Value; +struct ValueBuilder; + +enum class Data : uint8_t { + NONE = 0, + ToggleDataPoint = 1, + NumberDataPoint = 2, + TextDataPoint = 3, + SimpleData = 4, + CombinedData = 5, + ListData = 6, + ComplexData = 7, + MIN = NONE, + MAX = ComplexData +}; + +inline const Data (&EnumValuesData())[8] { + static const Data values[] = { + Data::NONE, + Data::ToggleDataPoint, + Data::NumberDataPoint, + Data::TextDataPoint, + Data::SimpleData, + Data::CombinedData, + Data::ListData, + Data::ComplexData + }; + return values; +} + +inline const char * const *EnumNamesData() { + static const char * const names[9] = { + "NONE", + "ToggleDataPoint", + "NumberDataPoint", + "TextDataPoint", + "SimpleData", + "CombinedData", + "ListData", + "ComplexData", + nullptr + }; + return names; +} + +inline const char *EnumNameData(Data e) { + if (::flatbuffers::IsOutRange(e, Data::NONE, Data::ComplexData)) return ""; + const size_t index = static_cast(e); + return EnumNamesData()[index]; +} + +template struct DataTraits { + static const Data enum_value = Data::NONE; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::ToggleDataPoint; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::NumberDataPoint; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::TextDataPoint; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::SimpleData; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::CombinedData; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::ListData; +}; + +template<> struct DataTraits { + static const Data enum_value = Data::ComplexData; +}; + +bool VerifyData(::flatbuffers::Verifier &verifier, const void *obj, Data type); +bool VerifyDataVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); + +enum class Schema : uint8_t { + NONE = 0, + ConstantSchema = 1, + SimpleSchema = 2, + CombinedSchema = 3, + ListSchema = 4, + ComplexSchema = 5, + MIN = NONE, + MAX = ComplexSchema +}; + +inline const Schema (&EnumValuesSchema())[6] { + static const Schema values[] = { + Schema::NONE, + Schema::ConstantSchema, + Schema::SimpleSchema, + Schema::CombinedSchema, + Schema::ListSchema, + Schema::ComplexSchema + }; + return values; +} + +inline const char * const *EnumNamesSchema() { + static const char * const names[7] = { + "NONE", + "ConstantSchema", + "SimpleSchema", + "CombinedSchema", + "ListSchema", + "ComplexSchema", + nullptr + }; + return names; +} + +inline const char *EnumNameSchema(Schema e) { + if (::flatbuffers::IsOutRange(e, Schema::NONE, Schema::ComplexSchema)) return ""; + const size_t index = static_cast(e); + return EnumNamesSchema()[index]; +} + +template struct SchemaTraits { + static const Schema enum_value = Schema::NONE; +}; + +template<> struct SchemaTraits { + static const Schema enum_value = Schema::ConstantSchema; +}; + +template<> struct SchemaTraits { + static const Schema enum_value = Schema::SimpleSchema; +}; + +template<> struct SchemaTraits { + static const Schema enum_value = Schema::CombinedSchema; +}; + +template<> struct SchemaTraits { + static const Schema enum_value = Schema::ListSchema; +}; + +template<> struct SchemaTraits { + static const Schema enum_value = Schema::ComplexSchema; +}; + +bool VerifySchema(::flatbuffers::Verifier &verifier, const void *obj, Schema type); +bool VerifySchemaVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); + +struct ToggleDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ToggleDataPointBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4 + }; + const ::flatbuffers::Vector *data() const { + return GetPointer *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.EndTable(); + } +}; + +struct ToggleDataPointBuilder { + typedef ToggleDataPoint Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::Vector> data) { + fbb_.AddOffset(ToggleDataPoint::VT_DATA, data); + } + explicit ToggleDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateToggleDataPoint( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> data = 0) { + ToggleDataPointBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +struct ToggleDataPoint::Traits { + using type = ToggleDataPoint; + static auto constexpr Create = CreateToggleDataPoint; +}; + +inline ::flatbuffers::Offset CreateToggleDataPointDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data = nullptr) { + auto data__ = data ? _fbb.CreateVector(*data) : 0; + return Ikarus::Value::CreateToggleDataPoint( + _fbb, + data__); +} + +struct NumberDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef NumberDataPointBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4 + }; + const ::flatbuffers::Vector *data() const { + return GetPointer *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.EndTable(); + } +}; + +struct NumberDataPointBuilder { + typedef NumberDataPoint Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::Vector> data) { + fbb_.AddOffset(NumberDataPoint::VT_DATA, data); + } + explicit NumberDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateNumberDataPoint( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> data = 0) { + NumberDataPointBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +struct NumberDataPoint::Traits { + using type = NumberDataPoint; + static auto constexpr Create = CreateNumberDataPoint; +}; + +inline ::flatbuffers::Offset CreateNumberDataPointDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data = nullptr) { + auto data__ = data ? _fbb.CreateVector(*data) : 0; + return Ikarus::Value::CreateNumberDataPoint( + _fbb, + data__); +} + +struct TextDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef TextDataPointBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4 + }; + const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *data() const { + return GetPointer> *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.VerifyVectorOfStrings(data()) && + verifier.EndTable(); + } +}; + +struct TextDataPointBuilder { + typedef TextDataPoint Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> data) { + fbb_.AddOffset(TextDataPoint::VT_DATA, data); + } + explicit TextDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateTextDataPoint( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> data = 0) { + TextDataPointBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +struct TextDataPoint::Traits { + using type = TextDataPoint; + static auto constexpr Create = CreateTextDataPoint; +}; + +inline ::flatbuffers::Offset CreateTextDataPointDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector<::flatbuffers::Offset<::flatbuffers::String>> *data = nullptr) { + auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::String>>(*data) : 0; + return Ikarus::Value::CreateTextDataPoint( + _fbb, + data__); +} + +struct ConstantSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ConstantSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SUB_SCHEMA_TYPE = 4, + VT_SUB_SCHEMA = 6, + VT_DATA_TYPE = 8, + VT_DATA = 10 + }; + Ikarus::Value::Schema sub_schema_type() const { + return static_cast(GetField(VT_SUB_SCHEMA_TYPE, 0)); + } + const void *sub_schema() const { + return GetPointer(VT_SUB_SCHEMA); + } + template const T *sub_schema_as() const; + const Ikarus::Value::ConstantSchema *sub_schema_as_ConstantSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *sub_schema_as_SimpleSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *sub_schema_as_CombinedSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::ListSchema *sub_schema_as_ListSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *sub_schema_as_ComplexSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(sub_schema()) : nullptr; + } + Ikarus::Value::Data data_type() const { + return static_cast(GetField(VT_DATA_TYPE, 0)); + } + const void *data() const { + return GetPointer(VT_DATA); + } + template const T *data_as() const; + const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { + return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { + return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { + return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::SimpleData *data_as_SimpleData() const { + return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::CombinedData *data_as_CombinedData() const { + return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ListData *data_as_ListData() const { + return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ComplexData *data_as_ComplexData() const { + return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SUB_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_SUB_SCHEMA) && + VerifySchema(verifier, sub_schema(), sub_schema_type()) && + VerifyField(verifier, VT_DATA_TYPE, 1) && + VerifyOffset(verifier, VT_DATA) && + VerifyData(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ConstantSchema *ConstantSchema::sub_schema_as() const { + return sub_schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *ConstantSchema::sub_schema_as() const { + return sub_schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *ConstantSchema::sub_schema_as() const { + return sub_schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *ConstantSchema::sub_schema_as() const { + return sub_schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *ConstantSchema::sub_schema_as() const { + return sub_schema_as_ComplexSchema(); +} + +template<> inline const Ikarus::Value::ToggleDataPoint *ConstantSchema::data_as() const { + return data_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *ConstantSchema::data_as() const { + return data_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *ConstantSchema::data_as() const { + return data_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *ConstantSchema::data_as() const { + return data_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *ConstantSchema::data_as() const { + return data_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *ConstantSchema::data_as() const { + return data_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *ConstantSchema::data_as() const { + return data_as_ComplexData(); +} + +struct ConstantSchemaBuilder { + typedef ConstantSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_sub_schema_type(Ikarus::Value::Schema sub_schema_type) { + fbb_.AddElement(ConstantSchema::VT_SUB_SCHEMA_TYPE, static_cast(sub_schema_type), 0); + } + void add_sub_schema(::flatbuffers::Offset sub_schema) { + fbb_.AddOffset(ConstantSchema::VT_SUB_SCHEMA, sub_schema); + } + void add_data_type(Ikarus::Value::Data data_type) { + fbb_.AddElement(ConstantSchema::VT_DATA_TYPE, static_cast(data_type), 0); + } + void add_data(::flatbuffers::Offset data) { + fbb_.AddOffset(ConstantSchema::VT_DATA, data); + } + explicit ConstantSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateConstantSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + Ikarus::Value::Schema sub_schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset sub_schema = 0, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset data = 0) { + ConstantSchemaBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_sub_schema(sub_schema); + builder_.add_data_type(data_type); + builder_.add_sub_schema_type(sub_schema_type); + return builder_.Finish(); +} + +struct ConstantSchema::Traits { + using type = ConstantSchema; + static auto constexpr Create = CreateConstantSchema; +}; + +struct SimpleSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef SimpleSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SUB_SCHEMA_TYPE = 4, + VT_SUB_SCHEMA = 6 + }; + Ikarus::Value::Schema sub_schema_type() const { + return static_cast(GetField(VT_SUB_SCHEMA_TYPE, 0)); + } + const void *sub_schema() const { + return GetPointer(VT_SUB_SCHEMA); + } + template const T *sub_schema_as() const; + const Ikarus::Value::ConstantSchema *sub_schema_as_ConstantSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *sub_schema_as_SimpleSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *sub_schema_as_CombinedSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::ListSchema *sub_schema_as_ListSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(sub_schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *sub_schema_as_ComplexSchema() const { + return sub_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(sub_schema()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SUB_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_SUB_SCHEMA) && + VerifySchema(verifier, sub_schema(), sub_schema_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ConstantSchema *SimpleSchema::sub_schema_as() const { + return sub_schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *SimpleSchema::sub_schema_as() const { + return sub_schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *SimpleSchema::sub_schema_as() const { + return sub_schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *SimpleSchema::sub_schema_as() const { + return sub_schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *SimpleSchema::sub_schema_as() const { + return sub_schema_as_ComplexSchema(); +} + +struct SimpleSchemaBuilder { + typedef SimpleSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_sub_schema_type(Ikarus::Value::Schema sub_schema_type) { + fbb_.AddElement(SimpleSchema::VT_SUB_SCHEMA_TYPE, static_cast(sub_schema_type), 0); + } + void add_sub_schema(::flatbuffers::Offset sub_schema) { + fbb_.AddOffset(SimpleSchema::VT_SUB_SCHEMA, sub_schema); + } + explicit SimpleSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateSimpleSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + Ikarus::Value::Schema sub_schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset sub_schema = 0) { + SimpleSchemaBuilder builder_(_fbb); + builder_.add_sub_schema(sub_schema); + builder_.add_sub_schema_type(sub_schema_type); + return builder_.Finish(); +} + +struct SimpleSchema::Traits { + using type = SimpleSchema; + static auto constexpr Create = CreateSimpleSchema; +}; + +struct SimpleData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef SimpleDataBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA_TYPE = 4, + VT_DATA = 6 + }; + Ikarus::Value::Data data_type() const { + return static_cast(GetField(VT_DATA_TYPE, 0)); + } + const void *data() const { + return GetPointer(VT_DATA); + } + template const T *data_as() const; + const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { + return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { + return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { + return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::SimpleData *data_as_SimpleData() const { + return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::CombinedData *data_as_CombinedData() const { + return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ListData *data_as_ListData() const { + return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ComplexData *data_as_ComplexData() const { + return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_DATA_TYPE, 1) && + VerifyOffset(verifier, VT_DATA) && + VerifyData(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ToggleDataPoint *SimpleData::data_as() const { + return data_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *SimpleData::data_as() const { + return data_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *SimpleData::data_as() const { + return data_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *SimpleData::data_as() const { + return data_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *SimpleData::data_as() const { + return data_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *SimpleData::data_as() const { + return data_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *SimpleData::data_as() const { + return data_as_ComplexData(); +} + +struct SimpleDataBuilder { + typedef SimpleData Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data_type(Ikarus::Value::Data data_type) { + fbb_.AddElement(SimpleData::VT_DATA_TYPE, static_cast(data_type), 0); + } + void add_data(::flatbuffers::Offset data) { + fbb_.AddOffset(SimpleData::VT_DATA, data); + } + explicit SimpleDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateSimpleData( + ::flatbuffers::FlatBufferBuilder &_fbb, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset data = 0) { + SimpleDataBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_data_type(data_type); + return builder_.Finish(); +} + +struct SimpleData::Traits { + using type = SimpleData; + static auto constexpr Create = CreateSimpleData; +}; + +struct CombinedSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef CombinedSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SCHEMAS_TYPE = 4, + VT_SCHEMAS = 6 + }; + const ::flatbuffers::Vector *schemas_type() const { + return GetPointer *>(VT_SCHEMAS_TYPE); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *schemas() const { + return GetPointer> *>(VT_SCHEMAS); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_SCHEMAS_TYPE) && + verifier.VerifyVector(schemas_type()) && + VerifyOffset(verifier, VT_SCHEMAS) && + verifier.VerifyVector(schemas()) && + VerifySchemaVector(verifier, schemas(), schemas_type()) && + verifier.EndTable(); + } +}; + +struct CombinedSchemaBuilder { + typedef CombinedSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_schemas_type(::flatbuffers::Offset<::flatbuffers::Vector> schemas_type) { + fbb_.AddOffset(CombinedSchema::VT_SCHEMAS_TYPE, schemas_type); + } + void add_schemas(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas) { + fbb_.AddOffset(CombinedSchema::VT_SCHEMAS, schemas); + } + explicit CombinedSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateCombinedSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> schemas_type = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas = 0) { + CombinedSchemaBuilder builder_(_fbb); + builder_.add_schemas(schemas); + builder_.add_schemas_type(schemas_type); + return builder_.Finish(); +} + +struct CombinedSchema::Traits { + using type = CombinedSchema; + static auto constexpr Create = CreateCombinedSchema; +}; + +inline ::flatbuffers::Offset CreateCombinedSchemaDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *schemas_type = nullptr, + const std::vector<::flatbuffers::Offset> *schemas = nullptr) { + auto schemas_type__ = schemas_type ? _fbb.CreateVector(*schemas_type) : 0; + auto schemas__ = schemas ? _fbb.CreateVector<::flatbuffers::Offset>(*schemas) : 0; + return Ikarus::Value::CreateCombinedSchema( + _fbb, + schemas_type__, + schemas__); +} + +struct CombinedData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef CombinedDataBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA_TYPE = 4, + VT_DATA = 6 + }; + const ::flatbuffers::Vector *data_type() const { + return GetPointer *>(VT_DATA_TYPE); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { + return GetPointer> *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA_TYPE) && + verifier.VerifyVector(data_type()) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + VerifyDataVector(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +struct CombinedDataBuilder { + typedef CombinedData Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data_type(::flatbuffers::Offset<::flatbuffers::Vector> data_type) { + fbb_.AddOffset(CombinedData::VT_DATA_TYPE, data_type); + } + void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { + fbb_.AddOffset(CombinedData::VT_DATA, data); + } + explicit CombinedDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateCombinedData( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> data_type = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { + CombinedDataBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_data_type(data_type); + return builder_.Finish(); +} + +struct CombinedData::Traits { + using type = CombinedData; + static auto constexpr Create = CreateCombinedData; +}; + +inline ::flatbuffers::Offset CreateCombinedDataDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data_type = nullptr, + const std::vector<::flatbuffers::Offset> *data = nullptr) { + auto data_type__ = data_type ? _fbb.CreateVector(*data_type) : 0; + auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; + return Ikarus::Value::CreateCombinedData( + _fbb, + data_type__, + data__); +} + +struct ListSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ListSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SCHEMA_TYPE = 4, + VT_SCHEMA = 6 + }; + Ikarus::Value::Schema schema_type() const { + return static_cast(GetField(VT_SCHEMA_TYPE, 0)); + } + const void *schema() const { + return GetPointer(VT_SCHEMA); + } + template const T *schema_as() const; + const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { + return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { + return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { + return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ListSchema *schema_as_ListSchema() const { + return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { + return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_SCHEMA) && + VerifySchema(verifier, schema(), schema_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ConstantSchema *ListSchema::schema_as() const { + return schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *ListSchema::schema_as() const { + return schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *ListSchema::schema_as() const { + return schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *ListSchema::schema_as() const { + return schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *ListSchema::schema_as() const { + return schema_as_ComplexSchema(); +} + +struct ListSchemaBuilder { + typedef ListSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_schema_type(Ikarus::Value::Schema schema_type) { + fbb_.AddElement(ListSchema::VT_SCHEMA_TYPE, static_cast(schema_type), 0); + } + void add_schema(::flatbuffers::Offset schema) { + fbb_.AddOffset(ListSchema::VT_SCHEMA, schema); + } + explicit ListSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateListSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset schema = 0) { + ListSchemaBuilder builder_(_fbb); + builder_.add_schema(schema); + builder_.add_schema_type(schema_type); + return builder_.Finish(); +} + +struct ListSchema::Traits { + using type = ListSchema; + static auto constexpr Create = CreateListSchema; +}; + +struct ListData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ListDataBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA_TYPE = 4, + VT_DATA = 6 + }; + const ::flatbuffers::Vector *data_type() const { + return GetPointer *>(VT_DATA_TYPE); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { + return GetPointer> *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA_TYPE) && + verifier.VerifyVector(data_type()) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + VerifyDataVector(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +struct ListDataBuilder { + typedef ListData Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data_type(::flatbuffers::Offset<::flatbuffers::Vector> data_type) { + fbb_.AddOffset(ListData::VT_DATA_TYPE, data_type); + } + void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { + fbb_.AddOffset(ListData::VT_DATA, data); + } + explicit ListDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateListData( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> data_type = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { + ListDataBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_data_type(data_type); + return builder_.Finish(); +} + +struct ListData::Traits { + using type = ListData; + static auto constexpr Create = CreateListData; +}; + +inline ::flatbuffers::Offset CreateListDataDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data_type = nullptr, + const std::vector<::flatbuffers::Offset> *data = nullptr) { + auto data_type__ = data_type ? _fbb.CreateVector(*data_type) : 0; + auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; + return Ikarus::Value::CreateListData( + _fbb, + data_type__, + data__); +} + +struct NamedSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef NamedSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_NAME = 4, + VT_SCHEMA_TYPE = 6, + VT_SCHEMA = 8 + }; + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + Ikarus::Value::Schema schema_type() const { + return static_cast(GetField(VT_SCHEMA_TYPE, 0)); + } + const void *schema() const { + return GetPointer(VT_SCHEMA); + } + template const T *schema_as() const; + const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { + return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { + return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { + return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ListSchema *schema_as_ListSchema() const { + return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { + return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_NAME) && + verifier.VerifyString(name()) && + VerifyField(verifier, VT_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_SCHEMA) && + VerifySchema(verifier, schema(), schema_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ConstantSchema *NamedSchema::schema_as() const { + return schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *NamedSchema::schema_as() const { + return schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *NamedSchema::schema_as() const { + return schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *NamedSchema::schema_as() const { + return schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *NamedSchema::schema_as() const { + return schema_as_ComplexSchema(); +} + +struct NamedSchemaBuilder { + typedef NamedSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { + fbb_.AddOffset(NamedSchema::VT_NAME, name); + } + void add_schema_type(Ikarus::Value::Schema schema_type) { + fbb_.AddElement(NamedSchema::VT_SCHEMA_TYPE, static_cast(schema_type), 0); + } + void add_schema(::flatbuffers::Offset schema) { + fbb_.AddOffset(NamedSchema::VT_SCHEMA, schema); + } + explicit NamedSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateNamedSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, + Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset schema = 0) { + NamedSchemaBuilder builder_(_fbb); + builder_.add_schema(schema); + builder_.add_name(name); + builder_.add_schema_type(schema_type); + return builder_.Finish(); +} + +struct NamedSchema::Traits { + using type = NamedSchema; + static auto constexpr Create = CreateNamedSchema; +}; + +inline ::flatbuffers::Offset CreateNamedSchemaDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset schema = 0) { + auto name__ = name ? _fbb.CreateString(name) : 0; + return Ikarus::Value::CreateNamedSchema( + _fbb, + name__, + schema_type, + schema); +} + +struct ComplexSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ComplexSchemaBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SCHEMAS = 4 + }; + const ::flatbuffers::Vector<::flatbuffers::Offset> *schemas() const { + return GetPointer> *>(VT_SCHEMAS); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_SCHEMAS) && + verifier.VerifyVector(schemas()) && + verifier.VerifyVectorOfTables(schemas()) && + verifier.EndTable(); + } +}; + +struct ComplexSchemaBuilder { + typedef ComplexSchema Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_schemas(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas) { + fbb_.AddOffset(ComplexSchema::VT_SCHEMAS, schemas); + } + explicit ComplexSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateComplexSchema( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas = 0) { + ComplexSchemaBuilder builder_(_fbb); + builder_.add_schemas(schemas); + return builder_.Finish(); +} + +struct ComplexSchema::Traits { + using type = ComplexSchema; + static auto constexpr Create = CreateComplexSchema; +}; + +inline ::flatbuffers::Offset CreateComplexSchemaDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector<::flatbuffers::Offset> *schemas = nullptr) { + auto schemas__ = schemas ? _fbb.CreateVector<::flatbuffers::Offset>(*schemas) : 0; + return Ikarus::Value::CreateComplexSchema( + _fbb, + schemas__); +} + +struct NamedData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef NamedDataBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_NAME = 4, + VT_DATA_TYPE = 6, + VT_DATA = 8 + }; + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); + } + Ikarus::Value::Data data_type() const { + return static_cast(GetField(VT_DATA_TYPE, 0)); + } + const void *data() const { + return GetPointer(VT_DATA); + } + template const T *data_as() const; + const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { + return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { + return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { + return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::SimpleData *data_as_SimpleData() const { + return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::CombinedData *data_as_CombinedData() const { + return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ListData *data_as_ListData() const { + return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ComplexData *data_as_ComplexData() const { + return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_NAME) && + verifier.VerifyString(name()) && + VerifyField(verifier, VT_DATA_TYPE, 1) && + VerifyOffset(verifier, VT_DATA) && + VerifyData(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ToggleDataPoint *NamedData::data_as() const { + return data_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *NamedData::data_as() const { + return data_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *NamedData::data_as() const { + return data_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *NamedData::data_as() const { + return data_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *NamedData::data_as() const { + return data_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *NamedData::data_as() const { + return data_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *NamedData::data_as() const { + return data_as_ComplexData(); +} + +struct NamedDataBuilder { + typedef NamedData Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { + fbb_.AddOffset(NamedData::VT_NAME, name); + } + void add_data_type(Ikarus::Value::Data data_type) { + fbb_.AddElement(NamedData::VT_DATA_TYPE, static_cast(data_type), 0); + } + void add_data(::flatbuffers::Offset data) { + fbb_.AddOffset(NamedData::VT_DATA, data); + } + explicit NamedDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateNamedData( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset data = 0) { + NamedDataBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_name(name); + builder_.add_data_type(data_type); + return builder_.Finish(); +} + +struct NamedData::Traits { + using type = NamedData; + static auto constexpr Create = CreateNamedData; +}; + +inline ::flatbuffers::Offset CreateNamedDataDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset data = 0) { + auto name__ = name ? _fbb.CreateString(name) : 0; + return Ikarus::Value::CreateNamedData( + _fbb, + name__, + data_type, + data); +} + +struct ComplexData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ComplexDataBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4 + }; + const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { + return GetPointer> *>(VT_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.VerifyVectorOfTables(data()) && + verifier.EndTable(); + } +}; + +struct ComplexDataBuilder { + typedef ComplexData Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { + fbb_.AddOffset(ComplexData::VT_DATA, data); + } + explicit ComplexDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateComplexData( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { + ComplexDataBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +struct ComplexData::Traits { + using type = ComplexData; + static auto constexpr Create = CreateComplexData; +}; + +inline ::flatbuffers::Offset CreateComplexDataDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector<::flatbuffers::Offset> *data = nullptr) { + auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; + return Ikarus::Value::CreateComplexData( + _fbb, + data__); +} + +struct Value FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef ValueBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SCHEMA_TYPE = 4, + VT_SCHEMA = 6, + VT_DATA_TYPE = 8, + VT_DATA = 10 + }; + Ikarus::Value::Schema schema_type() const { + return static_cast(GetField(VT_SCHEMA_TYPE, 0)); + } + const void *schema() const { + return GetPointer(VT_SCHEMA); + } + template const T *schema_as() const; + const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { + return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { + return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { + return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ListSchema *schema_as_ListSchema() const { + return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; + } + const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { + return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; + } + Ikarus::Value::Data data_type() const { + return static_cast(GetField(VT_DATA_TYPE, 0)); + } + const void *data() const { + return GetPointer(VT_DATA); + } + template const T *data_as() const; + const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { + return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { + return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { + return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; + } + const Ikarus::Value::SimpleData *data_as_SimpleData() const { + return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::CombinedData *data_as_CombinedData() const { + return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ListData *data_as_ListData() const { + return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; + } + const Ikarus::Value::ComplexData *data_as_ComplexData() const { + return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SCHEMA_TYPE, 1) && + VerifyOffset(verifier, VT_SCHEMA) && + VerifySchema(verifier, schema(), schema_type()) && + VerifyField(verifier, VT_DATA_TYPE, 1) && + VerifyOffset(verifier, VT_DATA) && + VerifyData(verifier, data(), data_type()) && + verifier.EndTable(); + } +}; + +template<> inline const Ikarus::Value::ConstantSchema *Value::schema_as() const { + return schema_as_ConstantSchema(); +} + +template<> inline const Ikarus::Value::SimpleSchema *Value::schema_as() const { + return schema_as_SimpleSchema(); +} + +template<> inline const Ikarus::Value::CombinedSchema *Value::schema_as() const { + return schema_as_CombinedSchema(); +} + +template<> inline const Ikarus::Value::ListSchema *Value::schema_as() const { + return schema_as_ListSchema(); +} + +template<> inline const Ikarus::Value::ComplexSchema *Value::schema_as() const { + return schema_as_ComplexSchema(); +} + +template<> inline const Ikarus::Value::ToggleDataPoint *Value::data_as() const { + return data_as_ToggleDataPoint(); +} + +template<> inline const Ikarus::Value::NumberDataPoint *Value::data_as() const { + return data_as_NumberDataPoint(); +} + +template<> inline const Ikarus::Value::TextDataPoint *Value::data_as() const { + return data_as_TextDataPoint(); +} + +template<> inline const Ikarus::Value::SimpleData *Value::data_as() const { + return data_as_SimpleData(); +} + +template<> inline const Ikarus::Value::CombinedData *Value::data_as() const { + return data_as_CombinedData(); +} + +template<> inline const Ikarus::Value::ListData *Value::data_as() const { + return data_as_ListData(); +} + +template<> inline const Ikarus::Value::ComplexData *Value::data_as() const { + return data_as_ComplexData(); +} + +struct ValueBuilder { + typedef Value Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_schema_type(Ikarus::Value::Schema schema_type) { + fbb_.AddElement(Value::VT_SCHEMA_TYPE, static_cast(schema_type), 0); + } + void add_schema(::flatbuffers::Offset schema) { + fbb_.AddOffset(Value::VT_SCHEMA, schema); + } + void add_data_type(Ikarus::Value::Data data_type) { + fbb_.AddElement(Value::VT_DATA_TYPE, static_cast(data_type), 0); + } + void add_data(::flatbuffers::Offset data) { + fbb_.AddOffset(Value::VT_DATA, data); + } + explicit ValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateValue( + ::flatbuffers::FlatBufferBuilder &_fbb, + Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, + ::flatbuffers::Offset schema = 0, + Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, + ::flatbuffers::Offset data = 0) { + ValueBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_schema(schema); + builder_.add_data_type(data_type); + builder_.add_schema_type(schema_type); + return builder_.Finish(); +} + +struct Value::Traits { + using type = Value; + static auto constexpr Create = CreateValue; +}; + +inline bool VerifyData(::flatbuffers::Verifier &verifier, const void *obj, Data type) { + switch (type) { + case Data::NONE: { + return true; + } + case Data::ToggleDataPoint: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::NumberDataPoint: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::TextDataPoint: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::SimpleData: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::CombinedData: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::ListData: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Data::ComplexData: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + default: return true; + } +} + +inline bool VerifyDataVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { + if (!values || !types) return !values && !types; + if (values->size() != types->size()) return false; + for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { + if (!VerifyData( + verifier, values->Get(i), types->GetEnum(i))) { + return false; + } + } + return true; +} + +inline bool VerifySchema(::flatbuffers::Verifier &verifier, const void *obj, Schema type) { + switch (type) { + case Schema::NONE: { + return true; + } + case Schema::ConstantSchema: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Schema::SimpleSchema: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Schema::CombinedSchema: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Schema::ListSchema: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Schema::ComplexSchema: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + default: return true; + } +} + +inline bool VerifySchemaVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { + if (!values || !types) return !values && !types; + if (values->size() != types->size()) return false; + for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { + if (!VerifySchema( + verifier, values->Get(i), types->GetEnum(i))) { + return false; + } + } + return true; +} + +inline const Ikarus::Value::Value *GetValue(const void *buf) { + return ::flatbuffers::GetRoot(buf); +} + +inline const Ikarus::Value::Value *GetSizePrefixedValue(const void *buf) { + return ::flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyValueBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedValueBuffer( + ::flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishValueBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedValueBuffer( + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace Value +} // namespace Ikarus + +#endif // FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_H_ diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index ec2087f..9ad444e 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -4,7 +4,6 @@ /// \author Folling #include -#include #include #include @@ -14,9 +13,10 @@ IKARUS_BEGIN_HEADER -/// \brief A blueprint object. -/// \details A blueprint is a collection of properties which can be linked to entities. -/// Each entity the blueprint is linked to will have values for the blueprints properties.q +/// \brief Blueprints are templates for managing common properties of entities. +/// \details A blueprint is a collection of properties which can be linked to entities. +/// Each entity the blueprint is linked to will have values for the blueprints +/// properties. Changes in blueprints will be reflected in all linked entities. struct IkarusBlueprint; /// \brief Creates a blueprint. @@ -25,19 +25,24 @@ struct IkarusBlueprint; /// \pre \li Must exist. /// \param name The name of the blueprint. /// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \pre \li Must be unique among all blueprints in the project. /// \param error_out \see errors.h /// \return The created blueprint or null if an error occurs. -IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); +IKA_API IkarusBlueprint * ikarus_blueprint_create( + struct IkarusProject * project, + char const * name, + IkarusErrorData * error_out +); -/// \brief Deletes & frees a blueprint. +/// \brief Deletes a blueprint. /// \param blueprint The blueprint to delete. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h /// \remark The blueprint must not be accessed after deletion. -IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * error_out); +IKA_API void ikarus_blueprint_delete( + IkarusBlueprint * blueprint, + IkarusErrorData * error_out +); /// \brief Gets the ID of a blueprint. /// \param blueprint The blueprint to get the ID of. @@ -45,23 +50,32 @@ IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorDat /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The ID of the blueprint or 0 if an error occurs. -IKA_API IkarusId ikarus_blueprint_get_id(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); +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. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The project the blueprint is part of or null if an error occurs. -IKA_API IkarusProject * ikarus_blueprint_get_project(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); +/// \return The project or null if an error occurs. +IKA_API IkarusProject * ikarus_blueprint_get_project( + IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +); /// \brief Gets the name of a blueprint. /// \param blueprint The blueprint 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 blueprint or null if an error occurs. -IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); +/// \return The name or null if an error occurs. +IKA_API char const * ikarus_blueprint_get_name( + IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +); /// \brief Sets the name of a blueprint. /// \param blueprint The blueprint to set the name of. @@ -69,10 +83,12 @@ IKA_API char const * ikarus_blueprint_get_name(IkarusBlueprint const * blueprint /// \pre \li Must exist. /// \param name The new name of the blueprint. /// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \pre \li Must be unique among all blueprints in the project. /// \param error_out \see errors.h -IKA_API void ikarus_blueprint_set_name(IkarusBlueprint * blueprint, char const * name, IkarusErrorData * error_out); +IKA_API void ikarus_blueprint_set_name( + IkarusBlueprint * blueprint, + char const * name, + IkarusErrorData * error_out +); /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. @@ -96,7 +112,10 @@ IKA_API void ikarus_blueprint_get_properties( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); +IKA_API size_t ikarus_blueprint_get_property_count( + IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +); /// \brief Gets the entities linked to a blueprint. /// \param blueprint The blueprint to get the linked entities of. @@ -120,7 +139,10 @@ IKA_API void ikarus_blueprint_get_linked_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of linked entities or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out); +IKA_API size_t ikarus_blueprint_get_linked_entity_count( + IkarusBlueprint const * blueprint, + IkarusErrorData * error_out +); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 04d59b0..28cebf8 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -14,25 +13,20 @@ IKARUS_BEGIN_HEADER /// \brief Entities are the core building blocks of Ikarus. -/// \detials Blueprints and Properties define the structure of the data. -/// Entities define the data itself. +/// \details Entities store two data: A name and a set of values. +/// The name identifies the entity but does not have to be unique. The values +/// are the actual information of the entity. /// -/// Properties can be associated with Entities in two ways: -/// - Directly: The property is linked to the entity. -/// - Indirectly: The property is linked to a blueprint the entity is linked to. +/// For documentation on the types and layout of values \see Values.h. /// -/// For each property an entity is linked to, it has a value. These values depend on the property's type. -/// For more information on the types see the property documentation. +/// Entities can be linked to blueprints. +/// The entity has a (possibly uninitialized) value for each property of all +/// blueprints it is linked to. \see Property.h \see Blueprint.h. /// -/// Values are the core type of data within Ikarus. -/// Each value is associated with one page and one property. -/// -/// \remark Values are typed, the type of a value is specified by its associated property. -/// For more information on the types see the property documentation. -/// -/// \remark Values are guaranteed to be in valid format for a given type -/// but not guaranteed to be valid under the settings of the property. -/// This is because changing the settings can invalidate existing values without resetting them. +/// We distinguish between `EntityValues` and `EntityPropertyValues`. +/// `EntityValues` are a direct part of the entity. +/// `EntityPropertyValues` are an indirect part of the entity, linked to them +/// via a blueprint. struct IkarusEntity; /// \brief Creates an entity. @@ -41,11 +35,13 @@ struct IkarusEntity; /// \pre \li Must exist. /// \param name The name of the entity. /// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \pre \li Must be unique among all entities in the project. /// \param error_out \see errors.h /// \return The created entity or null if an error occurs. -IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out); +IKA_API IkarusEntity * ikarus_entity_create( + struct IkarusProject * project, + char const * name, + IkarusErrorData * error_out +); /// \brief Deletes an entity. /// \param entity The entity to delete. @@ -53,15 +49,17 @@ IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char /// \pre \li Must exist. /// \param error_out \see errors.h /// \remark The entity must not be accessed after deletion. -IKA_API void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); +IKA_API void +ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); -/// \brief Gets the ID of an entity. -/// \param entity The entity to get the ID of. +/// \brief Gets the id of an entity. +/// \param entity The entity 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 entity or 0 if an error occurs. -IKA_API IkarusId ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out); +/// \return The id of the entity or 0 if an error occurs. +IKA_API int64_t +ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out); /// \brief Gets the project an entity is part of. /// \param entity The entity to get the project of. @@ -69,7 +67,10 @@ IKA_API IkarusId ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorDa /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The project the entity is part of or null if an error occurs. -IKA_API IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity, IkarusErrorData * error_out); +IKA_API IkarusProject * ikarus_entity_get_project( + IkarusEntity const * entity, + IkarusErrorData * error_out +); /// \brief Gets the name of an entity. /// \param entity The entity to get the name of. @@ -77,7 +78,10 @@ IKA_API IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity, I /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The name of the entity or null if an error occurs. -IKA_API char const * ikarus_entity_get_name(IkarusEntity const * entity, IkarusErrorData * error_out); +IKA_API char const * ikarus_entity_get_name( + IkarusEntity const * entity, + IkarusErrorData * error_out +); /// \brief Sets the name of an entity. /// \param entity The entity to set the name of. @@ -85,10 +89,12 @@ IKA_API char const * ikarus_entity_get_name(IkarusEntity const * entity, IkarusE /// \pre \li Must exist. /// \param name The new name of the entity. /// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \pre \li Must be unique among all entities in the project. /// \param error_out \see errors.h -IKA_API void ikarus_entity_set_name(IkarusEntity * entity, char const * name, IkarusErrorData * error_out); +IKA_API void ikarus_entity_set_name( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out +); /// \brief Checks if an entity is linked to a blueprint. /// \param entity The entity to check. @@ -99,8 +105,11 @@ IKA_API void ikarus_entity_set_name(IkarusEntity * entity, char const * name, Ik /// \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); +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. @@ -111,10 +120,14 @@ ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusB /// \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); +IKA_API void ikarus_entity_link_to_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusErrorData * error_out +); -/// \brief Unlinks an entity from a blueprint. All values of the properties of the blueprint the entity is linked with -/// will be deleted. +/// \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. @@ -123,9 +136,13 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru /// \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); +IKA_API void ikarus_entity_unlink_from_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusErrorData * error_out +); -/// \brief Gets the blueprints an entity is linked to. +/// \brief Gets all blueprints an entity is linked to. /// \param entity The entity to get the blueprints of. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -142,14 +159,80 @@ IKA_API void ikarus_entity_get_linked_blueprints( ); /// \brief Gets the number of blueprints an entity is linked to. -/// \param entity The entity to get the number of blueprints of. +/// \param entity The entity to get the number of linked blueprints of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_entity_get_linked_blueprint_count(IkarusEntity const * entity, IkarusErrorData * error_out); +IKA_API size_t ikarus_entity_get_linked_blueprint_count( + IkarusEntity const * entity, + IkarusErrorData * error_out +); -/// \brief Checks if an entity has a specific property. +/// \brief Checks if an entity has a value with a given name. +/// \param entity The entity to check. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the value to check. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return False if the entity does not have a value associated with +/// the name or if an error occurs, true otherwise. +IKA_API bool ikarus_entity_has_value( + IkarusEntity const * entity, + char const * name, + IkarusErrorData * error_out +); + +/// \brief Gets the value of an entity. +/// \param entity The entity to get the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the value to get. +/// \pre \li Must not be null. +/// \pre \li Must be an existing value of the entity. +/// \param error_out \see errors.h +/// \return The value of the entity or null if an error occurs. +IKA_API struct IkarusValue * ikarus_entity_get_value( + IkarusEntity const * entity, + char const * name, + IkarusErrorData * error_out +); + +/// \brief Sets the value of an entity. +/// If the entity does not have a value associated with the name, it is created. +/// \remark Types are overwritten if the value already exists. +/// \param entity The entity to set the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the value to set. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param value The new value of the entity. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +IKA_API void ikarus_entity_set_value( + IkarusEntity * entity, + char const * name, + struct IkarusValue const * value, + IkarusErrorData * error_out +); + +/// \brief Removes a value from an entity. +/// \pre \li The value must exist. +/// \param entity The entity to delete the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The name of the value to delete. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +IKA_API void ikarus_entity_remove_value( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out +); + +/// \brief Checks if a property is linked to an entity. /// \param entity The entity to check. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -157,14 +240,19 @@ IKA_API size_t ikarus_entity_get_linked_blueprint_count(IkarusEntity const * ent /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return False if an error occurs or the entity does not have the property, true otherwise. -IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); +/// \return False if an error occurs or the entity does not have the property, +/// true otherwise. +IKA_API bool ikarus_entity_has_property( + IkarusEntity const * entity, + struct IkarusProperty const * property, + IkarusErrorData * error_out +); -/// \brief Gets the properties of an entity. -/// \param entity The entity to get the properties of. +/// \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 properties to. +/// \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. @@ -176,30 +264,37 @@ IKA_API void ikarus_entity_get_properties( IkarusErrorData * error_out ); -/// \brief Gets the number of properties of an entity. -/// \param entity The entity to get the number of properties of. +/// \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); +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. -/// \details If the entity has never set the value of the property, the default value is returned (which may be undefined). +/// \details If the entity has never set the value of the property, +/// the property's default value is returned. /// \param entity The entity to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param property The property to get the value of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \pre \li Must be linked to the entity. /// \param error_out \see errors.h -/// \return The value of the property or null if the entity does not have the property or an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusEntityPropertyValue * -ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out); +/// \return The value of the property or null if an error occurs. +IKA_API struct IkarusValue * ikarus_entity_get_property_value( + IkarusEntity const * entity, + struct IkarusProperty const * property, + IkarusErrorData * error_out +); /// \brief Sets the value of a property of an entity. -/// \param entity The entity to set the value of. +/// \param entity The entity to set the property value of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param property The property to set the value of. @@ -211,7 +306,7 @@ ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const /// \pre \li Must be valid for the property's settings. /// \param error_out \see errors.h /// \remark If the entity does not have the property, this function fails. -IKA_API void ikarus_entity_set_value( +IKA_API void ikarus_entity_set_property_value( IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value, diff --git a/include/ikarus/objects/object.h b/include/ikarus/objects/object.h deleted file mode 100644 index 6a46e17..0000000 --- a/include/ikarus/objects/object.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -/// \file object.h -/// \author Folling - -#include -#include - -/// \defgroup object Objects -/// \brief Objects are a compound type of all types of objects in the database. -/// \details The following objects currently exist: -/// - \ref blueprint.h "Blueprints" -/// - \ref property.h "Properties" -/// - \ref entity.h "Entities" -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief A generic object. Wraps all types of objects, including folders. -struct IkarusObject; - -/// \brief Visits an object. Calling the appropriate function for the object's type. -/// \param object The object to visit. -/// \param blueprint_visitor The function to call if the object is a blueprint. Skipped if null. -/// \param property_visitor The function to call if the object is a property. Skipped if null. -/// \param entity_visitor The function to call if the object is an entity. Skipped if null. -/// \param data The data passed to the visitor functions. -/// \param error_out \see errors.h -IKA_API void ikarus_object_visit( - IkarusObject * object, - void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *), - void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *), - void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *), - void * data, - IkarusErrorData * error_out -); - -/// \see ikarus_object_visit -IKA_API void ikarus_object_visit_const( - IkarusObject const * object, - void (*blueprint_visitor)(struct IkarusBlueprint const *, IkarusErrorData * error_out, void *), - void (*property_visitor)(struct IkarusProperty const *, IkarusErrorData * error_out, void *), - void (*entity_visitor)(struct IkarusEntity const *, IkarusErrorData * error_out, void *), - void * data, - IkarusErrorData * error_out -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/object_type.h b/include/ikarus/objects/object_type.h deleted file mode 100644 index e30bb3c..0000000 --- a/include/ikarus/objects/object_type.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -/// \file object_type.h -/// \author Folling - -#include -#include - -/// \addtogroup objects Objects -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The type of an object. -enum IkarusObjectType { - /// \brief Not an object or no object. - IkarusObjectType_None = 0, - /// \brief An IkarusEntity. - IkarusObjectType_Entity = 0b00000001, - /// \brief An IkarusBlueprint. - IkarusObjectType_Blueprint = 0b00000010, - /// \brief An IkarusProperty. - IkarusObjectType_Property = 0b00000011, -}; - -/// \brief Converts an IkarusObjectType to a string. -/// \param type The type to convert. -/// \return The string representation of the type. -/// \remark The returned string must not be freed. -char const * ikarus_object_type_to_string(IkarusObjectType type); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h index 2fcf63b..d989ec2 100644 --- a/include/ikarus/objects/properties/number_property.h +++ b/include/ikarus/objects/properties/number_property.h @@ -20,7 +20,6 @@ struct IkarusNumberProperty; /// \pre \li Must exist. /// \param name The name of the property. /// \pre \li Must not be null. -/// \pre \li Must not be empty. /// \param property_source The property source to create the property for. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -36,31 +35,6 @@ IKA_API IkarusNumberProperty * ikarus_number_property_create( IkarusErrorData * error_out ); -/// \brief Sets the default value for a number property. -/// \param property The number property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The default value or null if an error occurs. -IKA_API struct IkarusNumberValue * -ikarus_number_property_get_default_value(struct IkarusNumberProperty * property, IkarusErrorData * error_out); - -/// \brief Sets the default value for a number property. -/// \param property The number property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \param error_out \see errors.h -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between -/// default values and other settings. -IKA_API void ikarus_number_property_set_default_value( - struct IkarusNumberProperty * property, - struct IkarusNumberValue * new_default_value, - IkarusErrorData * error_out -); - IKARUS_END_HEADER /// @} diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h index aa52b2c..c7ba19b 100644 --- a/include/ikarus/objects/properties/property.h +++ b/include/ikarus/objects/properties/property.h @@ -4,10 +4,10 @@ /// \author Folling #include -#include #include -#include #include +#include +#include /// \defgroup properties Properties /// \brief Properties define the structure and types of data. @@ -15,9 +15,10 @@ IKARUS_BEGIN_HEADER -/// \brief Properties are the placeholders of values for entities. -/// \details Each entity can have any number of properties. +/// \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 @@ -29,32 +30,10 @@ IKARUS_BEGIN_HEADER /// - Age (Number) /// - ISBN (Text) /// -/// Every property has settings which can be used to customise the property further. -/// Two settings that are shared among all properties are the following: -/// - List -/// - May be undefined -/// -/// Additionally, each property has a default value. If no default value is provided, a sensible default is chosen. -/// Setting a default value that isn't valid for the property is an error. Changing settings so that the current default -/// value becomes invalid is valid but unsets the custom default value. -/// -/// The former transforms a property into a list. Instead of one number, you could then specify a series of numbers. -/// The latter allows you to specify an "unknown" value for a property. -/// It might not be known if a character is dead or not for example. -/// /// Each entity associated with the property has a value for it. /// -/// Properties can also be added to blueprints in which case they are available for all entities associated with the -/// blueprint. -/// -/// We call properties within entities "Entity Properties" and properties within blueprints "Blueprint Properties". -/// -/// -/// \remark Properties are scoped to the blueprint or entity they are associated with. -/// \remark Values for properties are lazily created as space saving measure. +/// \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. -/// This default value is specified when the property is created and can be updated later. -/// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with. struct IkarusProperty; /// \brief Deletes a property. @@ -71,7 +50,7 @@ IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The ID of the property or 0 if an error occurs. -IKA_API IkarusId ikarus_property_get_id(IkarusProperty const * property, IkarusErrorData * error_out); +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. @@ -81,31 +60,66 @@ IKA_API IkarusId ikarus_property_get_id(IkarusProperty const * property, IkarusE /// \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. -IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out); +/// \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); -/// \brief Gets the source of a property. +// 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. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyScope const * ikarus_property_get_scope(IkarusProperty const * property, IkarusErrorData * error_out); - -/// \brief Gets the default value of a property. -/// \param property The property to get the type info of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The default value of the property or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out); +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. @@ -125,28 +139,6 @@ IKA_API void ikarus_property_visit( IkarusErrorData * error_out ); -/// \see ikarus_property_visit -IKA_API 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 -); - -/// \brief Casts a property to an object. -/// \param property The property to cast. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The property represented as an object or null if an error occurs. -/// \remark This operation is guaranteed to be very fast and is intended to be used frequently. -IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty * property, IkarusErrorData * error_out); - -/// \see ikarus_property_to_object -IKA_API struct IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property, IkarusErrorData * error_out); - IKARUS_END_HEADER // @} diff --git a/include/ikarus/objects/properties/property_scope.h b/include/ikarus/objects/properties/property_scope.h deleted file mode 100644 index a211e67..0000000 --- a/include/ikarus/objects/properties/property_scope.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -/// \file property_source.h -/// \author Folling - -#include -#include - -/// \addtogroup properties Properties -/// @{ - -IKARUS_BEGIN_HEADER - -struct IkarusPropertyScope; - -/// \brief Creates an blueprint property source. -/// \param blueprint The blueprint to create the property source for. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The created property source or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyScope * -ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint, IkarusErrorData * error_out); - -/// \brief Creates an entity property source. -/// \param entity The entity to create the property source for. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The created property source or null if an error occurs. -/// \remark Must be freed using #ikarus_free. -IKA_API struct IkarusPropertyScope * ikarus_property_source_create_entity(struct IkarusEntity * entity, IkarusErrorData * error_out); - -/// \brief Visits a property source, calling the appropriate callback. -/// \param property_source The property source to visit. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param blueprint_visitor The callback to call if the source is a blueprint, skipped if null. -/// \param entity_visitor The callback to call if the source is an entity, skipped if null. -/// \param user_data User data to pass to the callbacks. -/// \param error_out \see errors.h -IKA_API void ikarus_property_source_visit( - struct IkarusPropertyScope * property_source, - void (*blueprint_visitor)(struct IkarusBlueprint *, void *), - void (*entity_visitor)(struct IkarusEntity *, void *), - void * user_data, - IkarusErrorData * error_out -); - -/// \see ikarus_property_source_visit -IKA_API void ikarus_property_source_visit_const( - struct IkarusPropertyScope const * property_source, - void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), - void (*entity_visitor)(struct IkarusEntity const *, void *), - void * user_data, - IkarusErrorData * error_out -); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/property_type.h b/include/ikarus/objects/properties/property_type.h deleted file mode 100644 index 893cdca..0000000 --- a/include/ikarus/objects/properties/property_type.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -/// \file property_type.h -/// \author Folling - -#include - -/// \addtogroup properties Properties -/// @{ - -IKARUS_BEGIN_HEADER - -/// \brief The type of a property. -/// \details Designates the type of data stored by the property as well as which settings are -/// available. -enum IkarusPropertyType { - /// \brief A true/false boolean-esque value. - IkarusPropertyType_Toggle = 0x10000000, - /// \brief A numeric value, limited to IEEE 80 bit floating point numbers. - IkarusPropertyType_Number = 0x20000000, - /// \brief An arbitrary UTF-8 textual value. - IkarusPropertyType_Text = 0x30000000, -}; - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h index 5e59d92..a0c0a9d 100644 --- a/include/ikarus/objects/properties/text_property.h +++ b/include/ikarus/objects/properties/text_property.h @@ -20,7 +20,6 @@ struct IkarusTextProperty; /// \pre \li Must exist. /// \param name The name of the property. /// \pre \li Must not be null. -/// \pre \li Must not be empty. /// \param property_source The property source to create the property for. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -32,31 +31,7 @@ IKA_API IkarusTextProperty * ikarus_text_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, - struct IkarusTextValue* default_value, - IkarusErrorData * error_out -); - -/// \brief Sets the default value for a text property. -/// \param property The text property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The default value or null if an error occurs. -IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property, IkarusErrorData * error_out); - -/// \brief Sets the default value for a text property. -/// \param property The text property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \param error_out \see errors.h -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between -/// default values and other settings. -IKA_API void ikarus_text_property_set_default_value( - struct IkarusTextProperty * property, - struct IkarusTextValue * new_default_value, + struct IkarusTextValue * default_value, IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h index 403f392..bfec311 100644 --- a/include/ikarus/objects/properties/toggle_property.h +++ b/include/ikarus/objects/properties/toggle_property.h @@ -7,11 +7,11 @@ #include /// \addtogroup properties Properties -/// \brief Toggle properties store a value that can be either true or false. (e.g. "Is the character dead?") /// @{ 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. @@ -20,44 +20,15 @@ struct IkarusToggleProperty; /// \pre \li Must exist. /// \param name The name of the property. /// \pre \li Must not be null. -/// \pre \li Must not be empty. /// \param property_source The property source to create the property for. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param 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 IkarusToggleProperty * ikarus_toggle_property_create( struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source, - struct IkarusToggleValue * default_value, - IkarusErrorData * error_out -); - -/// \brief Sets the default value for a toggle property. -/// \param property The toggle property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param error_out \see errors.h -/// \return The default value or null if an error occurs. -IKA_API struct IkarusToggleValue * -ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, IkarusErrorData * error_out); - -/// \brief Sets the default value for a toggle property. -/// \param property The toggle property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param new_default_value The default value. -/// \pre \li Must not be null. -/// \pre \li Must be a valid value for the property. -/// \param error_out \see errors.h -/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between -/// default values and other settings. -IKA_API void ikarus_toggle_property_set_default_value( - struct IkarusToggleProperty * property, - struct IkarusToggleValue * new_default_value, IkarusErrorData * error_out ); diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 18a35f5..74258a9 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -30,7 +30,11 @@ struct IkarusProject; /// \return The created project or null if an error occurs. /// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_create(char const * path, char const * name, IkarusErrorData * error_out); +IKA_API IkarusProject * ikarus_project_create( + char const * path, + char const * name, + IkarusErrorData * error_out +); /// \brief Creates a project in memory. /// \param name The name of the project. Must neither be null nor empty. @@ -40,7 +44,8 @@ IKA_API IkarusProject * ikarus_project_create(char const * path, char const * na /// \return The created project or null if an error occurs. /// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out); +IKA_API IkarusProject * +ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out); /// \brief Opens an existing project. /// \param path The path to the project. @@ -50,7 +55,8 @@ IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name, Ikaru /// \return The opened project or null if an error occurs. /// \remark Must be freed using ikarus_free. Freeing does not delete the project from the filesystem. For that, use /// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_out); +IKA_API IkarusProject * +ikarus_project_open(char const * path, IkarusErrorData * error_out); /// \brief Gets the name of a project. /// \param project The project to get the name of. @@ -59,7 +65,10 @@ IKA_API IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * /// \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); +IKA_API char const * ikarus_project_get_name( + IkarusProject const * project, + IkarusErrorData * error_out +); /// \brief Sets the name of a project. /// \param project The project to set the name of. @@ -69,7 +78,11 @@ IKA_API char const * ikarus_project_get_name(IkarusProject const * project, Ikar /// \pre \li Must not be null. /// \pre \li Must not be empty. /// \param error_out \see errors.h -IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_name, IkarusErrorData * error_out); +IKA_API void ikarus_project_set_name( + IkarusProject * project, + char const * new_name, + IkarusErrorData * error_out +); /// \brief Gets the path of a project. /// \param project The project to get the path of. @@ -78,7 +91,10 @@ IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_n /// \param error_out \see errors.h /// \return The path of the project. /// \remark Ownership remains with libikarus, must not be freed. -IKA_API char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out); +IKA_API char const * ikarus_project_get_path( + IkarusProject const * project, + IkarusErrorData * error_out +); /// \brief Gets the entities of a project. /// \param project The project to get the entities of. @@ -101,7 +117,10 @@ IKA_API void ikarus_project_get_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of entities or undefined if an error occurs. -IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_entity_count( + IkarusProject const * project, + IkarusErrorData * error_out +); /// \brief Gets the blueprints of a project. /// \param project The project to get the blueprints of. @@ -124,51 +143,11 @@ IKA_API void ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); - -/// \brief Finds an entity by a given name. -/// \param project The project to search. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param name The name to search for. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param error_out \see errors.h -/// \return The entity with the given name or null if none was found. -IKA_API struct IkarusEntity * ikarus_project_get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); - -/// \brief Finds a property by a given name. -/// \param project The project to search. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param scope The scope of the property. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \remark Property names are unique only within their scope. -/// \param name The name to search for. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param error_out \see errors.h -/// \return The property with the given name or null if none was found. -IKA_API struct IkarusProperty * ikarus_project_get_property_by_name_and_scope( - IkarusProject * project, - struct IkarusPropertyScope * scope, - char const * name, +IKA_API size_t ikarus_project_get_blueprint_count( + IkarusProject const * project, IkarusErrorData * error_out ); -/// \brief Finds a blueprint by a given name. -/// \param project The project to search. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param name The name to search for. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. -/// \param error_out \see errors.h -/// \return The blueprint with the given name or null if none was found. -IKA_API struct IkarusBlueprint * -ikarus_project_get_blueprint_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); - IKARUS_END_HEADER /// @} diff --git a/include/ikarus/values/entity_property_value.h b/include/ikarus/values/entity_property_value.h deleted file mode 100644 index c408f00..0000000 --- a/include/ikarus/values/entity_property_value.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -/// \file entity_property_value.h -/// \author Folling - -/// \defgroup entity_property_values EntityPropertyValue -/// \brief Values in relation to an entity and one of its properties. -/// @{ - -#include -#include -#include - -IKARUS_BEGIN_HEADER - -/// \brief Like an \ref value.h "IkarusValue", but in relation to an entity and one of its properties -struct IkarusEntityPropertyValue; - -/// \brief Fetches the entity of an entity property value. -/// \param value The entity property value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return The entity of the entity property value. -/// \remark This value is owned by the entity property value and must not be freed directly. -struct IkarusEntity const * ikarus_entity_property_value_get_entity(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); - -/// \brief Fetches the property of an entity property value. -/// \param value The entity property value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return The property of the entity property value. -/// \remark This value is owned by the entity property value and must not be freed directly. -struct IkarusProperty const * -ikarus_entity_property_value_get_property(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); - -/// \brief Fetches the value of an entity property value. -/// \param value The entity property value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return The value of the entity property value. -/// \remark This value is owned by the entity property value and must not be freed directly. -struct IkarusValue const * ikarus_entity_property_value_get_value(IkarusEntityPropertyValue const * value, IkarusErrorData * error_out); - -IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h index 1425fed..220a11c 100644 --- a/include/ikarus/values/number_value.h +++ b/include/ikarus/values/number_value.h @@ -5,6 +5,7 @@ #include #include +#include /// \addtogroup values Values /// @{ @@ -15,10 +16,11 @@ IKARUS_BEGIN_HEADER 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. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusNumberValue * ikarus_number_value_create(IkarusErrorData * error_out); +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. @@ -26,14 +28,14 @@ IKA_API IkarusNumberValue * ikarus_number_value_create(IkarusErrorData * error_o /// \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 or the value is undefined. +/// \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 or the value is undefined. +/// \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. @@ -45,53 +47,37 @@ IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value, Ika /// \param error_out \see errors.h IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double new_data, IkarusErrorData * error_out); -/// \brief Removes a data from a number value. -/// \param value The number value. -/// \pre \li Must not be null. -/// \param idx The index of the data to remove. -/// \pre \li Must be less than the size of the value. -/// \param error_out \see errors.h -IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx, IkarusErrorData * error_out); - /// \brief Inserts a data into a number value. /// \param value The number value. /// \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 -/// \remark Noop if the value is undefined. IKA_API void ikarus_number_value_clear(IkarusNumberValue * value, IkarusErrorData * error_out); -/// \brief Checks if a number value is undefined. -/// \param value The number value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_number_value_is_undefined(IkarusNumberValue const * value, IkarusErrorData * error_out); - -/// \brief Changes a number value's undefined state. -/// \param value The number value. -/// \pre \li Must not be null. -/// \param undefined The new undefined state. -/// \param error_out \see errors.h -/// \remark Noop if the value is already undefined. -/// \remark If the value is set to undefined, all data will be cleared. -/// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined, IkarusErrorData * error_out); - /// \brief Converts a number value to a string. /// \param value The number value to convert. /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. -/// \remark Undefined if the value is undefined. IKA_API char const * ikarus_number_value_to_string(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. @@ -108,7 +94,6 @@ IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusN /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The copied value. -/// \remark Must be freed with #ikarus_free. IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value, IkarusErrorData * error_out); /// \brief Converts a number value to an entity value. @@ -117,10 +102,7 @@ IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * v /// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value, IkarusErrorData * error_out); - -/// \see ikarus_toggle_value_to_value -IKA_API struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value, IkarusErrorData * error_out); +IKA_API struct IkarusValueData * ikarus_number_value_to_value(IkarusNumberValue * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h index 6511eb5..3b7d978 100644 --- a/include/ikarus/values/text_value.h +++ b/include/ikarus/values/text_value.h @@ -5,36 +5,37 @@ #include #include -#include +#include /// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER -/// \brief A textual value. For example "Surname" or "Description" +/// \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. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusTextValue * ikarus_text_value_create(IkarusErrorData * error_out); +IKA_API IkarusTextValue * ikarus_text_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. +/// \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 null if an error occurs or the value is undefined. -IKA_API char const * const * ikarus_text_value_get(IkarusTextValue const * value, size_t idx, IkarusErrorData * error_out); +/// \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 or the value is undefined. +/// \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. @@ -42,59 +43,41 @@ IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value, IkarusE /// \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. Ownership remains with the caller. -/// \pre \li Must not be null. +/// \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 Inserts a data into a text value. -/// \param value The text value. -/// \pre \li Must not be null. -/// \param idx The index of the data to insert. -/// \pre \li Must be less than or equal to the size of the value. -/// \param new_data The new data. Ownership remains with the caller. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -IKA_API void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data, IkarusErrorData * error_out); - /// \brief Clears a text value. /// \param value The text value. +/// \pre \li Cardinality must be "Multiple". /// \param error_out \see errors.h -/// \remark Noop if the value is undefined. IKA_API void ikarus_text_value_clear(IkarusTextValue * value, IkarusErrorData * error_out); -/// \brief Checks if a text value is undefined. -/// \param value The text value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_text_value_is_undefined(IkarusTextValue const * value, IkarusErrorData * error_out); - -/// \brief Changes a text value's undefined state. -/// \param value The text value. -/// \pre \li Must not be null. -/// \param undefined The new undefined state. -/// \param error_out \see errors.h -/// \remark Noop if the value is already undefined. -/// \remark If the value is set to undefined, all data will be cleared. -/// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined, IkarusErrorData * error_out); - /// \brief Converts a text value to a string. /// \param value The text value to convert. /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. -/// \remark Undefined if the value is undefined. IKA_API char const * ikarus_text_value_to_string(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. @@ -111,7 +94,6 @@ IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextV /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The copied value. -/// \remark Must be freed with #ikarus_free. IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value, IkarusErrorData * error_out); /// \brief Converts a text value to an entity value. @@ -120,10 +102,7 @@ IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value, /// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value, IkarusErrorData * error_out); - -/// \see ikarus_text_value_to_value -IKA_API struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value, IkarusErrorData * error_out); +IKA_API struct IkarusValueData * ikarus_text_value_to_value(IkarusTextValue * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h index 9e1615b..05c5ddc 100644 --- a/include/ikarus/values/toggle_value.h +++ b/include/ikarus/values/toggle_value.h @@ -5,36 +5,37 @@ #include #include -#include +#include /// \addtogroup values Values /// @{ IKARUS_BEGIN_HEADER -/// \brief A true/false boolean-esque value. For example "Is Dead". +/// \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. -/// \remark Must be freed with #ikarus_free. -IKA_API IkarusToggleValue * ikarus_toggle_value_create(IkarusErrorData * error_out); +IKA_API IkarusToggleValue * ikarus_toggle_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. +/// \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 null if an error occurs or the value is undefined. -IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue const * value, size_t idx, IkarusErrorData * error_out); +/// \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 or the value is undefined. +/// \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. @@ -46,53 +47,37 @@ IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value, Ika /// \param error_out \see errors.h IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data, IkarusErrorData * error_out); -/// \brief Removes a data from a toggle value. -/// \param value The toggle value. -/// \pre \li Must not be null. -/// \param idx The index of the data to remove. -/// \pre \li Must be less than the size of the value. -/// \param error_out \see errors.h -IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx, IkarusErrorData * error_out); - /// \brief Inserts a data into a toggle value. /// \param value The toggle value. /// \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 -/// \remark Noop if the value is undefined. IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value, IkarusErrorData * error_out); -/// \brief Checks if a toggle value is undefined. -/// \param value The toggle value. -/// \pre \li Must not be null. -/// \param error_out \see errors.h -/// \return True if the value is undefined, false otherwise. -IKA_API bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value, IkarusErrorData * error_out); - -/// \brief Changes a toggle value's undefined state. -/// \param value The toggle value. -/// \pre \li Must not be null. -/// \param undefined The new undefined state. -/// \param error_out \see errors.h -/// \remark Noop if the value is already undefined. -/// \remark If the value is set to undefined, all data will be cleared. -/// \remark If the value is set to not undefined, the value is as if newly created. -IKA_API void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined, IkarusErrorData * error_out); - /// \brief Converts a toggle value to a string. /// \param value The toggle value to convert. /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The converted string. /// \remark Must be freed with #ikarus_free. -/// \remark Undefined if the value is undefined. IKA_API char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Checks if two values are equal. @@ -109,7 +94,6 @@ IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusT /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return The copied value. -/// \remark Must be freed with #ikarus_free. IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value, IkarusErrorData * error_out); /// \brief Converts a toggle value to an entity value. @@ -118,10 +102,7 @@ IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * v /// \param error_out \see errors.h /// \return The converted entity value. /// \remark This is the same pointer, so freeing it implies freeing the original value. -IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value, IkarusErrorData * error_out); - -/// \see ikarus_toggle_value_to_value -IKA_API struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value, IkarusErrorData * error_out); +IKA_API struct IkarusValueData * ikarus_toggle_value_to_value(IkarusToggleValue * value, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index fbc50e1..aec86ac 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -4,14 +4,27 @@ /// \author Folling /// \defgroup values Values -/// \brief The values of properties. -/// \details Each entity has a value for each property it is associated with. -/// These value classes represent plain objects. They are not associated with any entity. -/// Each value may be undefined. \see IkarusProperty -/// Values are stored as lists. If a property is "singular" then its value is a list of size 1. -/// Values are typed, with types existing for each of the corresponding property types. The data of values starts with the index 0. -/// When setting values for a property the type must match the property type and the value must be valid under the -/// property's settings. \see PropertyType +/// \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. 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 @@ -19,15 +32,19 @@ IKARUS_BEGIN_HEADER -/// \brief The common type for all values. +/// \brief The common type for all value data. struct IkarusValue; -/// \brief Visits an entity value, calling the appropriate function for the value's type. +/// \brief Visits an entity value, +/// calling the appropriate function for the value's type. /// \param value The entity value to visit. /// \pre \li Must not be null. -/// \param toggle_visitor The function to call if the value is a toggle value. Skipped if null. -/// \param number_visitor The function to call if the value is a number value. Skipped if null. -/// \param text_visitor The function to call if the value is a text value. Skipped if null. +/// \param toggle_visitor The function to call if the value is a toggle value. +/// \remark Skipped if null. +/// \param number_visitor The function to call if the value is a number value. +/// \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( @@ -39,16 +56,6 @@ IKA_API void ikarus_value_visit( IkarusErrorData * error_out ); -/// \see ikarus_value_visit -IKA_API void ikarus_value_visit_const( - IkarusValue const * value, - void (*toggle_visitor)(struct IkarusToggleValue const *, void *), - void (*number_visitor)(struct IkarusNumberValue const *, void *), - void (*text_visitor)(struct IkarusTextValue const *, void *), - void * data, - IkarusErrorData * error_out -); - IKARUS_END_HEADER /// @} diff --git a/include/ikarus/values/value_cardinality.h b/include/ikarus/values/value_cardinality.h new file mode 100644 index 0000000..6d5526f --- /dev/null +++ b/include/ikarus/values/value_cardinality.h @@ -0,0 +1,23 @@ +#pragma once + +/// \file value_cardinality.h +/// \author Folling + +/// \addtogroup values Values +/// @{ + +#include + +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 + +/// @} diff --git a/include/ikarus/values/value_type.h b/include/ikarus/values/value_type.h new file mode 100644 index 0000000..caa6165 --- /dev/null +++ b/include/ikarus/values/value_type.h @@ -0,0 +1,198 @@ +#pragma once + +/// \file value_schema.h +/// \author Folling + +/// \addtogroup values Values +/// @{ + +#include +#include +#include + +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, Number, Complex<"Foo":Toggle,"Bar":Text> +struct IkarusValueLayout; +/// \brief A fixed datapoint with a fixed layout. +/// Example: Age: Union> +struct IkarusValueLayoutConstant; +/// \brief A singular datapoint with one of a list of layouts. +/// Example: ChestReward: Combination +struct IkarusValueLayoutCombination; +/// \brief A collection of datapoints with a homogenous layout. +/// Example: Friends: List +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 + +/// @} diff --git a/src/ikarus/errors.cpp b/src/ikarus/errors.cpp index 35014f8..8f8ff3c 100644 --- a/src/ikarus/errors.cpp +++ b/src/ikarus/errors.cpp @@ -1,13 +1,5 @@ #include "ikarus/errors.h" -#include - -#include - -#include - -#include - char const * ikarus_get_error_info_name(IkarusErrorInfo info) { switch (info) { case IkarusErrorInfo_None: return "None"; diff --git a/src/ikarus/errors.hpp b/src/ikarus/errors.hpp index 0a9941f..e12a68f 100644 --- a/src/ikarus/errors.hpp +++ b/src/ikarus/errors.hpp @@ -78,16 +78,3 @@ inline void safe_strcpy(char * dest, std::string_view src, size_t dest_size) { #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__); - -#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("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); diff --git a/src/ikarus/global.cpp b/src/ikarus/global.cpp deleted file mode 100644 index 8b1929a..0000000 --- a/src/ikarus/global.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "ikarus/global.h" - -#include - -void ikarus_free(void * ptr) { - std::free(ptr); -} \ No newline at end of file diff --git a/src/ikarus/id.cpp b/src/ikarus/id.cpp deleted file mode 100644 index 9749667..0000000 --- a/src/ikarus/id.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "ikarus/id.h" - -#include - -uint64_t IKARUS_ID_OBJECT_TYPE_BITS = 8; -uint64_t IKARUS_ID_OBJECT_RANDOM_BITS = sizeof(IkarusId) * 8 - IKARUS_ID_OBJECT_TYPE_BITS; - -IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type) { - return data | (static_cast(type) << IKARUS_ID_OBJECT_RANDOM_BITS); -} - -IkarusObjectType ikarus_id_get_object_type(IkarusId id) { - return static_cast(id >> IKARUS_ID_OBJECT_RANDOM_BITS); -} diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index a1cf9e5..a2a5673 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -11,24 +11,27 @@ #include #include #include +#include -IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): +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_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); IKARUS_VTRYRV_OR_FAIL( - IkarusId const id, + int64_t const id, nullptr, "failed to create blueprint: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?)", IkarusObjectType_Blueprint)); - auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint); - TRY(db->execute("INSERT INTO `blueprints`(`id`, `name`) VALUES(?, ?)", id, name)); - return cppbase::ok(id); + project->db->transact([name](auto * db) -> cppbase::Result { + TRY(db->execute("INSERT INTO `blueprints`(`name`) VALUES(?)", name)); + return cppbase::ok(db->last_insert_rowid()); }) ); @@ -43,13 +46,13 @@ void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * erro , "unable to delete blueprint: {}", IkarusErrorInfo_Database_QueryFailed, - blueprint->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id) + blueprint->project->db->execute("DELETE FROM `blueprints` WHERE `id` = ?", blueprint->id) ); blueprint->project->uncache(blueprint); } -IkarusId ikarus_blueprint_get_id(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) { +int64_t ikarus_blueprint_get_id(IkarusBlueprint const * blueprint, IkarusErrorData * error_out) { return ikarus::util::object_get_id(blueprint, error_out); } @@ -79,14 +82,14 @@ void ikarus_blueprint_get_properties( return; } - std::tuple ids_and_types[properties_out_size]; + std::tuple 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( - "SELECT `id` FROM `properties` WHERE `source` = ?", + blueprint->project->db->query_many_buffered( + "SELECT `id` FROM `properties` WHERE `blueprint` = ?", ids_and_types, properties_out_size, blueprint->id @@ -109,7 +112,7 @@ size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint, Ik 0, "unable to fetch blueprint property count from database: {}", IkarusErrorInfo_Database_QueryFailed, - blueprint->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", blueprint->id) + blueprint->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `blueprint` = ?", blueprint->id) ); return ret; @@ -129,13 +132,13 @@ void ikarus_blueprint_get_linked_entities( return; } - IkarusId ids[entities_out_size]; + 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( + blueprint->project->db->query_many_buffered( "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", ids, entities_out_size, @@ -157,7 +160,8 @@ size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprin 0, "unable to fetch blueprint linked entity count from database: {}", IkarusErrorInfo_Database_QueryFailed, - blueprint->project->db->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) + blueprint->project->db + ->query_one("SELECT COUNT(`entity`) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) ); return ret; diff --git a/src/ikarus/objects/blueprint.hpp b/src/ikarus/objects/blueprint.hpp index 30c57b0..cd46d62 100644 --- a/src/ikarus/objects/blueprint.hpp +++ b/src/ikarus/objects/blueprint.hpp @@ -2,9 +2,9 @@ #include -struct IkarusBlueprint : IkarusObject { +struct IkarusBlueprint : public IkarusObject { public: - IkarusBlueprint(struct IkarusProject * project, IkarusId id); + IkarusBlueprint(struct IkarusProject * project, int64_t id); IkarusBlueprint(IkarusBlueprint const &) = default; IkarusBlueprint(IkarusBlueprint &&) = default; @@ -15,7 +15,5 @@ public: ~IkarusBlueprint() override = default; public: - inline std::string_view get_table_name() const noexcept override { - return "blueprints"; - } + std::string_view get_table_name() const noexcept override; }; diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index e1dbf5a..2df1404 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -10,22 +10,38 @@ #include #include #include +#include -IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { +IkarusEntity::IkarusEntity(IkarusProject * project, int64_t id): + IkarusObject{project, id} {} + +std::string_view IkarusEntity::get_table_name() const noexcept { + return "entities"; +} + +IkarusEntity * ikarus_entity_create( + struct IkarusProject * project, + char const * name, + IkarusErrorData * error_out +) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, static_cast(nullptr), nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); IKARUS_VTRYRV_OR_FAIL( - IkarusId const id, + int64_t const id, nullptr, "failed to create entity: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->transact([name](auto * db) -> cppbase::Result { - TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?)", IkarusObjectType_Entity)); - auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Entity); - TRY(db->execute("INSERT INTO `entities`(`id`, `name`) VALUES(?, ?)", id, name)); - return cppbase::ok(id); - }) + project->db->transact( + [name](auto * db + ) -> cppbase::Result { + TRY(db->execute( + "INSERT INTO `entities`(`name`) VALUES(?, ?)", + name + )); + return cppbase::ok(db->last_insert_rowid()); + } + ) ); return project->get_entity(id); @@ -39,25 +55,37 @@ void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { , "unable to delete entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", entity->id) + entity->project->db + ->execute("DELETE FROM `entities` WHERE `id` = ?", entity->id) ); entity->project->uncache(entity); } -IkarusId ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out) { +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) { +IkarusProject * ikarus_entity_get_project( + IkarusEntity const * entity, + IkarusErrorData * error_out +) { return ikarus::util::object_get_project(entity, error_out); } -char const * ikarus_entity_get_name(IkarusEntity const * entity, IkarusErrorData * error_out) { +char const * ikarus_entity_get_name( + IkarusEntity const * entity, + IkarusErrorData * error_out +) { return ikarus::util::object_get_name(entity, error_out); } -void ikarus_entity_set_name(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) { +void ikarus_entity_set_name( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out +) { ikarus::util::object_set_name(entity, name, error_out); } @@ -77,7 +105,9 @@ bool ikarus_entity_is_linked_to_blueprint( "unable to check whether entity is linked to blueprint", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_one( - "SELECT EXISTS(SELECT 1 FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?)", + "SELECT EXISTS(SELECT 1 FROM `entity_blueprint_links` WHERE " + "`entity` = ? AND " + "`blueprint` = ?)", entity->id, blueprint->id ) @@ -86,7 +116,11 @@ bool ikarus_entity_is_linked_to_blueprint( return ret; } -void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint, IkarusErrorData * error_out) { +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, ); @@ -97,14 +131,20 @@ void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBluepri "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", + "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) { +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, ); @@ -114,9 +154,26 @@ void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct IkarusBlu , "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) + 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( @@ -133,14 +190,15 @@ void ikarus_entity_get_linked_blueprints( return; } - IkarusId ids[blueprints_out_size]; + 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( - "SELECT `blueprint` FROM `entity_blueprint_links` WHERE `entity` = ?", + entity->project->db->query_many_buffered( + "SELECT `blueprint` FROM `entity_blueprint_links` WHERE `entity` = " + "?", ids, blueprints_out_size, entity->id @@ -152,7 +210,10 @@ void ikarus_entity_get_linked_blueprints( } } -size_t ikarus_entity_get_linked_blueprint_count(IkarusEntity const * entity, IkarusErrorData * error_out) { +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); @@ -161,31 +222,144 @@ size_t ikarus_entity_get_linked_blueprint_count(IkarusEntity const * entity, Ika 0, "unable to fetch entity linked blueprint count from database: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", entity->id) + entity->project->db->query_one( + "SELECT COUNT(`blueprint`) FROM `entity_blueprint_links` WHERE " + "`entity` = ?", + entity->id + ) ); return ret; } -bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out) { +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( + "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( + IkarusEntity * entity, + char const * name, + struct IkarusValue const * value, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, ); + IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); + IKARUS_FAIL_IF_NAME_INVALID(name, ); + 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 ret, + auto const has_property, false, "unable to check whether entity has property: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_one( - "SELECT EXISTS(SELECT 1 FROM `entity_properties` WHERE `entity` = ? AND `property` = ?)", + "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 ret; + return has_property; } void ikarus_entity_get_properties( @@ -202,19 +376,25 @@ void ikarus_entity_get_properties( return; } - std::tuple ids_and_types[properties_out_size]; + std::tuple 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 entity properties from database: {}", + "unable to fetch properties from entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->query_many_buffered( - "SELECT `property`, `type` FROM `properties` WHERE `source` = ?", + entity->project->db->query_many_buffered( + "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]; @@ -222,23 +402,39 @@ void ikarus_entity_get_properties( } } -size_t ikarus_entity_get_property_count(IkarusEntity const * entity, IkarusErrorData * error_out) { +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 ret, + size_t const count, 0, - "unable to fetch entity property count from database: {}", + "unable to fetch property count from entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", entity->id) + entity->project->db->query_one( + "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 ret; + return count; } -struct IkarusEntityPropertyValue * -ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out) { +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); @@ -247,25 +443,27 @@ ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const auto * value = fetch_value_from_db( entity->project, error_out, - "SELECT IFNULL((SELECT `value` FROM `values` WHERE `entity` = ? AND `property` = ?), (SELECT `default_value` FROM `properties` WHERE `id` = ?))", + "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, property->id ); IKARUS_FAIL_IF_ERROR(nullptr); - return new IkarusEntityPropertyValue{ - .entity = entity, - .property = property, - .value = value, - }; + return value; } -void ikarus_entity_set_value( +void ikarus_entity_set_property_value( IkarusEntity * entity, struct IkarusProperty const * property, - struct IkarusEntityPropertyValue * value, + struct IkarusValue * value, IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(entity, ); @@ -274,18 +472,17 @@ void ikarus_entity_set_value( IKARUS_FAIL_IF_OBJECT_MISSING(property, ); IKARUS_FAIL_IF_NULL(value, ); - auto value_json_str = boost::json::serialize(value->value->to_json()); - IKARUS_TRYRV_OR_FAIL( , "unable to set entity property value: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->execute( - "INSERT INTO `values`(`entity`, `property`, `value`) VALUES(?, ?, ?) ON CONFLICT(`entity`, `property`) DO UPDATE SET `value` = ?", + "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, - value_json_str, - value_json_str + boost::json::serialize(value->to_json()) ) ); } diff --git a/src/ikarus/objects/entity.hpp b/src/ikarus/objects/entity.hpp index 586c88f..e3bc035 100644 --- a/src/ikarus/objects/entity.hpp +++ b/src/ikarus/objects/entity.hpp @@ -2,10 +2,9 @@ #include -struct IkarusEntity : IkarusObject { +struct IkarusEntity : public IkarusObject { public: - inline IkarusEntity(struct IkarusProject * project, IkarusId id): - IkarusObject{project, id} {} + inline IkarusEntity(struct IkarusProject * project, int64_t id); IkarusEntity(IkarusEntity const &) = default; IkarusEntity(IkarusEntity &&) = default; @@ -16,7 +15,5 @@ public: ~IkarusEntity() override = default; public: - inline std::string_view get_table_name() const noexcept override { - return "entities"; - } + std::string_view get_table_name() const noexcept override; }; diff --git a/src/ikarus/objects/object.cpp b/src/ikarus/objects/object.cpp index d233783..01747cc 100644 --- a/src/ikarus/objects/object.cpp +++ b/src/ikarus/objects/object.cpp @@ -1,93 +1,7 @@ #include "object.hpp" -#include - -#include - -#include -#include -#include -#include -#include -#include #include -IkarusObject::IkarusObject(IkarusProject * project, IkarusId id): +IkarusObject::IkarusObject(IkarusProject * project, int64_t id): project{project}, id{id} {} - -void ikarus_object_visit( - IkarusObject * object, - void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *), - void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *), - void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *), - void * data, - IkarusErrorData * error_out -) { - struct Data { - void (*blueprint_visitor)(struct IkarusBlueprint *, IkarusErrorData * error_out, void *); - void (*property_visitor)(struct IkarusProperty *, IkarusErrorData * error_out, void *); - void (*entity_visitor)(struct IkarusEntity *, IkarusErrorData * error_out, void *); - void * data; - }; - - Data passthru_data{blueprint_visitor, property_visitor, entity_visitor, data}; - - ikarus_object_visit_const( - object, - [](IkarusBlueprint const * blueprint, IkarusErrorData * error_out, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->blueprint_visitor(const_cast(blueprint), error_out, passthru_data->data); - }, - [](IkarusProperty const * property, IkarusErrorData * error_out, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->property_visitor(const_cast(property), error_out, passthru_data->data); - }, - [](IkarusEntity const * entity, IkarusErrorData * error_out, void * data) { - auto const * passthru_data = static_cast(data); - passthru_data->entity_visitor(const_cast(entity), error_out, passthru_data->data); - }, - &passthru_data, - error_out - ); -} - -void ikarus_object_visit_const( - IkarusObject const * object, - void (*blueprint_visitor)(struct IkarusBlueprint const *, IkarusErrorData * error_out, void *), - void (*property_visitor)(struct IkarusProperty const *, IkarusErrorData * error_out, void *), - void (*entity_visitor)(struct IkarusEntity const *, IkarusErrorData * error_out, void *), - void * data, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(object, ); - IKARUS_FAIL_IF_OBJECT_MISSING(object, ); - - switch (ikarus_id_get_object_type(object->id)) { - case IkarusObjectType_Entity: { - auto const * entity = dynamic_cast(object); - IKARUS_FAIL_IF(entity == nullptr, , "object with entity id wasn't a entity", IkarusErrorInfo_LibIkarus_InvalidState); - entity_visitor(entity, error_out, data); - } - case IkarusObjectType_Blueprint: { - auto const * blueprint = dynamic_cast(object); - IKARUS_FAIL_IF(blueprint == nullptr, , "object with blueprint id wasn't a blueprint", IkarusErrorInfo_LibIkarus_InvalidState); - blueprint_visitor(blueprint, error_out, data); - } - case IkarusObjectType_Property: { - auto const * property = dynamic_cast(object); - IKARUS_FAIL_IF(property == nullptr, , "object with property id wasn't a property", IkarusErrorInfo_LibIkarus_InvalidState); - property_visitor(property, error_out, data); - } - default: { - IKARUS_FAIL( - , - fmt::format("unknown object type: {}", ikarus_object_type_to_string(ikarus_id_get_object_type(object->id))), - IkarusErrorInfo_LibIkarus_InvalidState - ); - } - } - - // not needed, but a good delineation of our intention if this function gets extended - IKARUS_FAIL_IF_ERROR() -} diff --git a/src/ikarus/objects/object.hpp b/src/ikarus/objects/object.hpp index 20eed59..96d5911 100644 --- a/src/ikarus/objects/object.hpp +++ b/src/ikarus/objects/object.hpp @@ -2,11 +2,9 @@ #include -#include - -struct IkarusObject { +class IkarusObject { public: - IkarusObject(struct IkarusProject * project, IkarusId id); + IkarusObject(struct IkarusProject * project, int64_t id); IkarusObject(IkarusObject const &) = default; IkarusObject(IkarusObject &&) = default; @@ -21,5 +19,18 @@ public: public: struct IkarusProject * project; - IkarusId id; + 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("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); diff --git a/src/ikarus/objects/object_type.cpp b/src/ikarus/objects/object_type.cpp deleted file mode 100644 index fb7d825..0000000 --- a/src/ikarus/objects/object_type.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "ikarus/objects/object_type.h" - -char const * ikarus_object_type_to_string(IkarusObjectType type) { - switch (type) { - case IkarusObjectType_None: return "none"; - case IkarusObjectType_Entity: return "entity"; - case IkarusObjectType_Blueprint: return "blueprint"; - case IkarusObjectType_Property: return "property"; - } - - return "unknown"; -} diff --git a/src/ikarus/objects/properties/number_property.cpp b/src/ikarus/objects/properties/number_property.cpp index 43a966c..60c9ccd 100644 --- a/src/ikarus/objects/properties/number_property.cpp +++ b/src/ikarus/objects/properties/number_property.cpp @@ -7,7 +7,7 @@ #include #include -IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, IkarusId id): +IkarusNumberProperty::IkarusNumberProperty(IkarusProject * project, int64_t id): IkarusProperty{project, id, this} {} IkarusNumberProperty * ikarus_number_property_create( diff --git a/src/ikarus/objects/properties/number_property.hpp b/src/ikarus/objects/properties/number_property.hpp index eddb189..55ff25c 100644 --- a/src/ikarus/objects/properties/number_property.hpp +++ b/src/ikarus/objects/properties/number_property.hpp @@ -1,14 +1,14 @@ #pragma once #include -#include #include +#include -struct IkarusNumberProperty : IkarusProperty { +struct IkarusNumberProperty : public IkarusProperty { public: using value_type = IkarusNumberValue; - constexpr auto static PropertyType = IkarusPropertyType_Number; + constexpr auto static PropertyType = IkarusValueType_Number; public: - IkarusNumberProperty(struct IkarusProject * project, IkarusId id); + IkarusNumberProperty(struct IkarusProject * project, int64_t id); }; diff --git a/src/ikarus/objects/properties/property.cpp b/src/ikarus/objects/properties/property.cpp index 929d3a6..ecc2f3e 100644 --- a/src/ikarus/objects/properties/property.cpp +++ b/src/ikarus/objects/properties/property.cpp @@ -5,12 +5,12 @@ #include #include #include -#include +#include #include #include #include -IkarusProperty::IkarusProperty(IkarusProject * project, IkarusId id, Data data): +IkarusProperty::IkarusProperty(IkarusProject * project, int64_t id, Data data): IkarusObject{project, id}, data{data} {} @@ -28,7 +28,7 @@ IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * property->project->uncache(property); } -IkarusId ikarus_property_get_id(IkarusProperty const * property, IkarusErrorData * error_out) { +int64_t ikarus_property_get_id(IkarusProperty const * property, IkarusErrorData * error_out) { return ikarus::util::object_get_id(property, error_out); } @@ -44,19 +44,19 @@ void ikarus_property_set_name(IkarusProperty * property, char const * name, Ikar ikarus::util::object_set_name(property, name, error_out); } -IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(property, IkarusPropertyType_Toggle); - IKARUS_FAIL_IF_OBJECT_MISSING(property, IkarusPropertyType_Toggle); +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, - IkarusPropertyType_Toggle, + IkarusValueType_Toggle, "unable to fetch property type from database: {}", IkarusErrorInfo_Database_QueryFailed, property->project->db->query_one("SELECT `type` FROM `properties` WHERE `id` = ?", property->id) ); - return static_cast(ret); + return static_cast(ret); } IkarusPropertyScope const * ikarus_property_get_scope(IkarusProperty const * property, IkarusErrorData * error_out) { @@ -68,7 +68,7 @@ IkarusPropertyScope const * ikarus_property_get_scope(IkarusProperty const * pro nullptr, "unable to fetch property source from database: {}", IkarusErrorInfo_Database_QueryFailed, - property->project->db->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) + property->project->db->query_one("SELECT `source` FROM `properties` WHERE `id` = ?", property->id) ); switch (ikarus_id_get_object_type(source)) { @@ -83,7 +83,7 @@ IkarusPropertyScope const * ikarus_property_get_scope(IkarusProperty const * pro } } -IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out) { +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); diff --git a/src/ikarus/objects/properties/property.hpp b/src/ikarus/objects/properties/property.hpp index 5c37d7c..1fd0394 100644 --- a/src/ikarus/objects/properties/property.hpp +++ b/src/ikarus/objects/properties/property.hpp @@ -4,12 +4,12 @@ #include -struct IkarusProperty : IkarusObject { +struct IkarusProperty : public IkarusObject { public: using Data = std::variant; public: - IkarusProperty(struct IkarusProject * project, IkarusId id, Data data); + IkarusProperty(struct IkarusProject * project, int64_t id, Data data); IkarusProperty(IkarusProperty const &) = default; IkarusProperty(IkarusProperty &&) = default; diff --git a/src/ikarus/objects/properties/property_scope.cpp b/src/ikarus/objects/properties/property_scope.cpp deleted file mode 100644 index b8ec32f..0000000 --- a/src/ikarus/objects/properties/property_scope.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "property_scope.hpp" - -#include - -#include -#include - -IkarusPropertyScope::IkarusPropertyScope(Data data): - data{data} {} - -IkarusId IkarusPropertyScope::get_id() const { - return std::visit( - cppbase::overloaded{ - [](IkarusBlueprint const * blueprint) { return blueprint->id; }, - [](IkarusEntity const * entity) { return entity->id; } - }, - data - ); -} - -IkarusPropertyScope * ikarus_property_source_create_blueprint(IkarusBlueprint * blueprint) { - return new IkarusPropertyScope{blueprint}; -} - -IkarusPropertyScope * ikarus_property_source_create_entity(IkarusEntity * entity) { - return new IkarusPropertyScope{entity}; -} - -void ikarus_property_source_visit( - struct IkarusPropertyScope * property_source, - void (*blueprint_visitor)(struct IkarusBlueprint *, void *), - void (*entity_visitor)(struct IkarusEntity *, void *), - void * user_data -) { - std::visit( - cppbase::overloaded{ - [blueprint_visitor, user_data](IkarusBlueprint * blueprint) { blueprint_visitor(blueprint, user_data); }, - [entity_visitor, user_data](IkarusEntity * entity) { entity_visitor(entity, user_data); } - }, - property_source->data - ); -} - -void ikarus_property_source_visit_const( - struct IkarusPropertyScope const * property_source, - void (*blueprint_visitor)(struct IkarusBlueprint const *, void *), - void (*entity_visitor)(struct IkarusEntity const *, void *), - void * user_data -) { - std::visit( - cppbase::overloaded{ - [blueprint_visitor, user_data](IkarusBlueprint const * blueprint) { blueprint_visitor(blueprint, user_data); }, - [entity_visitor, user_data](IkarusEntity const * entity) { entity_visitor(entity, user_data); } - }, - property_source->data - ); -} diff --git a/src/ikarus/objects/properties/property_scope.hpp b/src/ikarus/objects/properties/property_scope.hpp deleted file mode 100644 index ab8832a..0000000 --- a/src/ikarus/objects/properties/property_scope.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include - -#include -#include - -#define IKARUS_FAIL_IF_PROPERTY_SCOPE_INVALID(scope, ret) \ - std::visit( \ - cppbase::overloaded{[error_out](auto const * object) { \ - IKARUS_FAIL_IF_NULL(object, ); \ - IKARUS_FAIL_IF_OBJECT_MISSING(object, ); \ - }}, \ - scope->data \ - ); \ - \ - IKARUS_FAIL_IF_ERROR(nullptr); - -struct IkarusPropertyScope { -public: - using Data = std::variant; - -public: - explicit IkarusPropertyScope(Data data); - - IkarusPropertyScope(IkarusPropertyScope const &) = default; - IkarusPropertyScope(IkarusPropertyScope &&) = default; - - IkarusPropertyScope & operator=(IkarusPropertyScope const &) = default; - IkarusPropertyScope & operator=(IkarusPropertyScope &&) = default; - - virtual ~IkarusPropertyScope() = default; - -public: - [[nodiscard]] IkarusId get_id() const; - -public: - Data data; -}; diff --git a/src/ikarus/objects/properties/text_property.cpp b/src/ikarus/objects/properties/text_property.cpp index 6aaf483..c51d653 100644 --- a/src/ikarus/objects/properties/text_property.cpp +++ b/src/ikarus/objects/properties/text_property.cpp @@ -7,7 +7,7 @@ #include #include -IkarusTextProperty::IkarusTextProperty(IkarusProject * project, IkarusId id): +IkarusTextProperty::IkarusTextProperty(IkarusProject * project, int64_t id): IkarusProperty{project, id, this} {} IkarusTextProperty * ikarus_text_property_create( diff --git a/src/ikarus/objects/properties/text_property.hpp b/src/ikarus/objects/properties/text_property.hpp index aca2cfa..b690cda 100644 --- a/src/ikarus/objects/properties/text_property.hpp +++ b/src/ikarus/objects/properties/text_property.hpp @@ -1,14 +1,14 @@ #pragma once #include -#include +#include #include -struct IkarusTextProperty : IkarusProperty { +struct IkarusTextProperty : public IkarusProperty { public: using value_type = IkarusTextValue; - constexpr auto static PropertyType = IkarusPropertyType_Text; + constexpr auto static PropertyType = IkarusValueType_Text; public: - IkarusTextProperty(struct IkarusProject * project, IkarusId id); + IkarusTextProperty(struct IkarusProject * project, int64_t id); }; diff --git a/src/ikarus/objects/properties/toggle_property.cpp b/src/ikarus/objects/properties/toggle_property.cpp index 2449b39..d1705a2 100644 --- a/src/ikarus/objects/properties/toggle_property.cpp +++ b/src/ikarus/objects/properties/toggle_property.cpp @@ -7,7 +7,7 @@ #include #include -IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, IkarusId id): +IkarusToggleProperty::IkarusToggleProperty(IkarusProject * project, int64_t id): IkarusProperty{project, id, this} {} IkarusToggleProperty * ikarus_toggle_property_create( diff --git a/src/ikarus/objects/properties/toggle_property.hpp b/src/ikarus/objects/properties/toggle_property.hpp index bf53c19..35575ee 100644 --- a/src/ikarus/objects/properties/toggle_property.hpp +++ b/src/ikarus/objects/properties/toggle_property.hpp @@ -1,14 +1,17 @@ #pragma once #include -#include #include +#include -struct IkarusToggleProperty : IkarusProperty { +struct IkarusToggleProperty { public: using value_type = IkarusToggleValue; - constexpr auto static PropertyType = IkarusPropertyType_Toggle; + constexpr auto static PropertyType = IkarusValueType_Toggle; public: - IkarusToggleProperty(struct IkarusProject * project, IkarusId id); + IkarusToggleProperty(struct IkarusProject * project, int64_t id); + +public: + IkarusProperty * property; }; diff --git a/src/ikarus/objects/properties/util.hpp b/src/ikarus/objects/properties/util.hpp index c095a18..97146f1 100644 --- a/src/ikarus/objects/properties/util.hpp +++ b/src/ikarus/objects/properties/util.hpp @@ -22,15 +22,14 @@ T * create_property( ) { IKARUS_FAIL_IF_NULL(project, nullptr); IKARUS_FAIL_IF_NULL(property_scope, nullptr); - IKARUS_FAIL_IF_PROPERTY_SCOPE_INVALID(property_scope, nullptr); - IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, property_scope, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); IKARUS_VTRYRV_OR_FAIL( - IkarusId const id, + int64_t const id, nullptr, "failed to create property: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->transact([name, property_scope](auto * db) -> cppbase::Result { + project->db->transact([name, property_scope](auto * db) -> cppbase::Result { 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( diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp index 1de035a..e625f5d 100644 --- a/src/ikarus/objects/util.hpp +++ b/src/ikarus/objects/util.hpp @@ -6,18 +6,12 @@ #include #include -#include -#include -#include -#include -#include -#include #include namespace ikarus::util { template -[[nodiscard]] IkarusId object_get_id(O const * object, IkarusErrorData * error_out) { +[[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); @@ -49,81 +43,14 @@ template return strdup(ret.data()); } -[[nodiscard]] inline bool name_is_unique( - IkarusProject const * project, - std::string_view name, - [[maybe_unused]] IkarusBlueprint const * blueprint, - IkarusErrorData * error_out -) { - IKARUS_VTRYRV_OR_FAIL( - bool const exists, - false, - "unable to check if blueprint name is unique", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_one("SELECT EXISTS(SELECT 1 FROM `blueprints` WHERE `name` = ?)", name) - ); - - return !exists; -} - -[[nodiscard]] inline bool name_is_unique( - IkarusProject const * project, - std::string_view name, - [[maybe_unused]] IkarusEntity const * entity, - IkarusErrorData * error_out -) { - IKARUS_VTRYRV_OR_FAIL( - bool const exists, - false, - "unable to check if entity name is unique", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_one("SELECT EXISTS(SELECT 1 FROM `entities` WHERE `name` = ?)", name) - ); - - return !exists; -} - -[[nodiscard]] inline bool -name_is_unique(IkarusProject const * project, std::string_view name, IkarusPropertyScope const * scope, IkarusErrorData * error_out) { - IKARUS_VTRYRV_OR_FAIL( - bool const exists, - false, - "unable to check if property name is unique", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_one("SELECT EXISTS(SELECT 1 FROM `properties` WHERE `name` = ? AND `scope` = ?)", name, scope->get_id()) - ); - - return !exists; -} - -[[nodiscard]] inline bool -name_is_unique(IkarusProject const * project, std::string_view name, IkarusProperty const * property, IkarusErrorData * error_out) { - std::unique_ptr scope{ikarus_property_get_scope(property, error_out)}; - IKARUS_FAIL_IF_ERROR(false); - - return name_is_unique(project, name, scope.get(), error_out); -} - -#define IKARUS_FAIL_IF_NAME_INVALID(name, ret) \ - IKARUS_FAIL_IF_NULL(name, ret); \ - IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); - -#define IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, object, ret, ...) \ - IKARUS_FAIL_IF_NAME_INVALID(name, ret); \ - IKARUS_FAIL_IF( \ - !ikarus::util::name_is_unique(project, name, object, error_out), \ - ret, \ - "name must be unique", \ - IkarusErrorInfo_Client_InvalidInput \ - ); \ - IKARUS_FAIL_IF_ERROR(ret); +#define IKARUS_FAIL_IF_NAME_INVALID(name, ret) IKARUS_FAIL_IF_NULL(name, ret); template 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_OR_DUPLICATE(name, object->project, object, ); + IKARUS_FAIL_IF_NAME_INVALID(name, ); IKARUS_TRYRV_OR_FAIL( , diff --git a/src/ikarus/persistence/migrations.hpp b/src/ikarus/persistence/migrations.hpp index a46d1ee..db649a5 100644 --- a/src/ikarus/persistence/migrations.hpp +++ b/src/ikarus/persistence/migrations.hpp @@ -8,11 +8,12 @@ #include namespace ikarus { + CPPBASE_ASSET(m0_initial_layout, "ikarus/persistence/migrations/m0_initial_layout.sql"); class Migration : public sqlitecpp::Migration { public: - Migration(char const * sql, size_t size): + Migration(char const * sql, size_t const size): sql{sql, size} {} ~Migration() override = default; diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index 9bf3cc7..aa28c8a 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -1,68 +1,51 @@ -CREATE TABLE `objects` -( - `do_not_access_rowid_alias` INTEGER PRIMARY KEY, - `type` INT NOT NULL, - `id` INT GENERATED ALWAYS AS (`do_not_access_rowid_alias` | (`type` << 56)) VIRTUAL UNIQUE -) STRICT; - -CREATE UNIQUE INDEX `object_id` ON `objects` (`id`); -CREATE INDEX `object_type` ON `objects` (`type`); - CREATE TABLE `entities` ( - `id` INT, - `name` TEXT NOT NULL, + `id` INTEGER PRIMARY KEY, + `name` TEXT NOT NULL +) STRICT; - PRIMARY KEY (`id`), - UNIQUE (`name`), - FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE +CREATE TABLE `entity_values` +( + `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, + `name` TEXT NOT NULL, + `type` INT NOT NULL, + `value` TEXT NOT NULL, + + PRIMARY KEY (`entity`, `name`) ) WITHOUT ROWID, STRICT; CREATE TABLE `blueprints` ( - `id` INT, - `name` TEXT NOT NULL, - - PRIMARY KEY (`id`), - UNIQUE (`name`), - FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE -) WITHOUT ROWID, STRICT; - -CREATE TABLE `entity_blueprint_links` -( - `entity` INT NOT NULL, - `blueprint` INT NOT NULL, - - PRIMARY KEY (`entity`, `blueprint`), - FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, - FOREIGN KEY (`blueprint`) REFERENCES `blueprints` (`id`) ON DELETE CASCADE -) WITHOUT ROWID, STRICT; + `id` INTEGER PRIMARY KEY, + `name` TEXT NOT NULL +) STRICT; CREATE TABLE `properties` ( - `id` INT, - `name` TEXT NOT NULL, - `type` INT NOT NULL, - `default_value` TEXT NOT NULL, - `settings` TEXT NOT NULL, - `source` INT NOT NULL, - - PRIMARY KEY (`id`), - UNIQUE (`source`, `name`), - FOREIGN KEY (`id`) REFERENCES `objects` (`id`) ON DELETE CASCADE, - FOREIGN KEY (`source`) REFERENCES `objects` (`id`) ON DELETE CASCADE -) WITHOUT ROWID, STRICT; + `id` INTEGER PRIMARY KEY, + `blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE, + `name` TEXT NOT NULL, + `type` INT NOT NULL, + `cardinality` INT NOT NULL, + `default_value` TEXT NOT NULL, + `settings` TEXT NOT NULL +) STRICT; CREATE INDEX `properties_type` ON `properties` (`type`); -CREATE INDEX `properties_source` ON `properties` (`source`); -CREATE TABLE `values` +CREATE TABLE `entity_blueprint_links` ( - `entity` INT NOT NULL, - `property` INT NOT NULL, - `value` TEXT NOT NULL, + `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, + `blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE, - PRIMARY KEY (`entity`, `property`), - FOREIGN KEY (`entity`) REFERENCES `entities` (`id`) ON DELETE CASCADE, - FOREIGN KEY (`property`) REFERENCES `properties` (`id`) ON DELETE CASCADE + PRIMARY KEY (`entity`, `blueprint`) +) STRICT; + +CREATE TABLE `entity_property_values` +( + `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, + `property` INTEGER NOT NULL REFERENCES `properties` (`id`) ON DELETE CASCADE, + `value` TEXT NOT NULL, + + PRIMARY KEY (`entity`, `property`) ) WITHOUT ROWID, STRICT; diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 87ebef5..0d5b1e5 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -22,7 +22,7 @@ IkarusProject::IkarusProject(std::string_view name, std::string_view path, std:: _properties{}, _entities{} {} -IkarusBlueprint * IkarusProject::get_blueprint(IkarusId id) { +IkarusBlueprint * IkarusProject::get_blueprint(int64_t id) { return get_cached_object(id, this->_blueprints); } @@ -30,7 +30,7 @@ auto IkarusProject::uncache(IkarusBlueprint * blueprint) -> void { remove_cached_object(blueprint, _blueprints); } -auto IkarusProject::get_entity(IkarusId id) -> IkarusEntity * { +auto IkarusProject::get_entity(int64_t id) -> IkarusEntity * { return get_cached_object(id, this->_entities); } @@ -38,16 +38,16 @@ auto IkarusProject::uncache(IkarusEntity * entity) -> void { remove_cached_object(entity, _entities); } -auto IkarusProject::get_property(IkarusId id, IkarusPropertyType type) -> IkarusProperty * { +auto IkarusProject::get_property(int64_t id, IkarusValueType type) -> IkarusProperty * { auto const iter = _properties.find(id); if (iter == _properties.cend()) { switch (type) { - case IkarusPropertyType_Toggle: + case IkarusValueType_Toggle: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); - case IkarusPropertyType_Number: + case IkarusValueType_Number: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); - case IkarusPropertyType_Text: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); + case IkarusValueType_Text: return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); } } @@ -212,13 +212,13 @@ void ikarus_project_get_blueprints( return; } - IkarusId ids[blueprints_out_size]; + int64_t ids[blueprints_out_size]; IKARUS_TRYRV_OR_FAIL( , "unable to fetch project blueprints from database: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered("SELECT `id` FROM `blueprints`", ids, blueprints_out_size) + project->db->query_many_buffered("SELECT `id` FROM `blueprints`", ids, blueprints_out_size) ); for (size_t i = 0; i < blueprints_out_size; ++i) { @@ -253,13 +253,13 @@ void ikarus_project_get_entities( return; } - IkarusId ids[entities_out_size]; + int64_t ids[entities_out_size]; IKARUS_TRYRV_OR_FAIL( , "unable to fetch project entities from database: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered("SELECT `id` FROM `entities`", ids, entities_out_size) + project->db->query_many_buffered("SELECT `id` FROM `entities`", ids, entities_out_size) ); for (size_t i = 0; i < entities_out_size; ++i) { @@ -293,7 +293,7 @@ struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * n nullptr, "unable to find entity in database: {}", IkarusErrorInfo_Client_InvalidInput, - project->db->query_one("SELECT `id` FROM `entities` WHERE `name` = ?", name) + project->db->query_one("SELECT `id` FROM `entities` WHERE `name` = ?", name) ); return project->get_entity(id); @@ -310,7 +310,7 @@ get_property_by_name(IkarusProject * project, IkarusPropertyScope * scope, char nullptr, "unable to find property in database: {}", IkarusErrorInfo_Client_InvalidInput, - project->db->query_one( + project->db->query_one( "SELECT `id`, `type` FROM `properties` WHERE `name` = ? AND `scope` = ?", name, scope->get_id() @@ -332,7 +332,7 @@ IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * n nullptr, "unable to find blueprint in database: {}", IkarusErrorInfo_Client_InvalidInput, - project->db->query_one("SELECT `id` FROM `blueprints` WHERE `name` = ?", name) + project->db->query_one("SELECT `id` FROM `blueprints` WHERE `name` = ?", name) ); return project->get_blueprint(id); diff --git a/src/ikarus/persistence/project.hpp b/src/ikarus/persistence/project.hpp index f5a00a1..1dc69dc 100644 --- a/src/ikarus/persistence/project.hpp +++ b/src/ikarus/persistence/project.hpp @@ -1,7 +1,5 @@ #pragma once -#include -#include #include #include @@ -9,8 +7,7 @@ #include #include -#include -#include +#include namespace fs = boost::filesystem; @@ -20,19 +17,19 @@ public: IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db); public: - [[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *; + [[nodiscard]] auto get_blueprint(int64_t id) -> struct IkarusBlueprint *; auto uncache(struct IkarusBlueprint * blueprint) -> void; - [[nodiscard]] auto get_entity(IkarusId id) -> struct IkarusEntity *; + [[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(IkarusId id, IkarusPropertyType type) -> struct IkarusProperty *; + [[nodiscard]] auto get_property(int64_t id, IkarusValueType type) -> struct IkarusProperty *; auto uncache(struct IkarusProperty * property) -> void; private: template - [[nodiscard]] T * get_cached_object(IkarusId id, auto & cache) { + [[nodiscard]] T * get_cached_object(int64_t id, auto & cache) { auto const iter = cache.find(id); if (iter == cache.cend()) { @@ -43,7 +40,7 @@ private: } template - void remove_cached_object(T * object, std::unordered_map> & cache) { + void remove_cached_object(T * object, std::unordered_map> & cache) { cache.erase(object->id); } @@ -53,9 +50,9 @@ public: std::unique_ptr db; private: - std::unordered_map> mutable _blueprints; - std::unordered_map> mutable _properties; - std::unordered_map> mutable _entities; + std::unordered_map> mutable _blueprints; + std::unordered_map> mutable _properties; + std::unordered_map> mutable _entities; }; constexpr std::string_view DB_PROJECT_NAME_KEY = "PROJECT_NAME"; diff --git a/src/ikarus/values/entity_property_value.hpp b/src/ikarus/values/entity_property_value.hpp index f75d6ba..6f70f09 100644 --- a/src/ikarus/values/entity_property_value.hpp +++ b/src/ikarus/values/entity_property_value.hpp @@ -1,7 +1 @@ #pragma once - -struct IkarusEntityPropertyValue { - struct IkarusEntity const * entity; - struct IkarusProperty const * property; - struct IkarusValue const * value; -}; diff --git a/src/ikarus/values/number_value.cpp b/src/ikarus/values/number_value.cpp index 37eab49..41fba42 100644 --- a/src/ikarus/values/number_value.cpp +++ b/src/ikarus/values/number_value.cpp @@ -6,60 +6,60 @@ #include IkarusNumberValue::IkarusNumberValue(): - IkarusValue{this} {} + IkarusValueData{this} {} -IkarusNumberValue * ikarus_number_value_create() { +IkarusNumberValue * ikarus_number_value_data_create() { return new IkarusNumberValue{}; } -double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx) { - return ikarus_value_base_get(value, idx); +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_get_size(IkarusNumberValue const * value) { - return ikarus_value_base_get_size(value); +size_t ikarus_number_value_data_get_size(IkarusNumberValue const * value) { + return ikarus_value_data_base_get_size(value); } -void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double new_data) { - return ikarus_value_base_set(value, idx, new_data); +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_remove(IkarusNumberValue * value, size_t idx) { - return ikarus_value_base_remove(value, idx); +void ikarus_number_value_data_remove(IkarusNumberValue * value, size_t idx) { + return ikarus_value_data_base_remove(value, idx); } -void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, long double new_data) { - return ikarus_value_base_insert(value, idx, new_data); +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_clear(IkarusNumberValue * value) { - return ikarus_value_base_clear(value); +void ikarus_number_value_data_clear(IkarusNumberValue * value) { + return ikarus_value_data_base_clear(value); } -bool ikarus_number_value_is_undefined(IkarusNumberValue const * value) { - return ikarus_value_base_is_undefined(value); +bool ikarus_number_value_data_is_undefined(IkarusNumberValue const * value) { + return ikarus_value_data_base_is_undefined(value); } -void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined) { - return ikarus_value_base_set_undefined(value, undefined); +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_to_string(IkarusNumberValue const * value) { - return ikarus_value_base_to_string(value, [](auto const & value) { return value; }); +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_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs) { - return ikarus_value_base_is_equal(lhs, rhs); +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_copy(IkarusNumberValue const * value) { - return ikarus_value_base_copy(value); +IkarusNumberValue * ikarus_number_value_data_copy(IkarusNumberValue const * value) { + return ikarus_value_data_base_copy(value); } -struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value) { - return ikarus_value_base_to_value(value); +struct IkarusValueData * ikarus_number_value_data_to_value(IkarusNumberValue * value) { + return ikarus_value_data_base_to_value(value); } -struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value) { - return ikarus_value_base_to_value_const(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); } diff --git a/src/ikarus/values/number_value.hpp b/src/ikarus/values/number_value.hpp index 6c6cb70..bd4bec5 100644 --- a/src/ikarus/values/number_value.hpp +++ b/src/ikarus/values/number_value.hpp @@ -5,7 +5,7 @@ #include -struct IkarusNumberValue : IkarusValue { +struct IkarusNumberValue : IkarusValueData { public: using DataType = double; diff --git a/src/ikarus/values/text_value.cpp b/src/ikarus/values/text_value.cpp index 8235c6d..9400f2e 100644 --- a/src/ikarus/values/text_value.cpp +++ b/src/ikarus/values/text_value.cpp @@ -7,60 +7,60 @@ #include IkarusTextValue::IkarusTextValue(): - IkarusValue{this} {} + IkarusValueData{this} {} -IkarusTextValue * ikarus_text_value_create() { +IkarusTextValue * ikarus_text_value_data_create() { return new IkarusTextValue{}; } -char const * ikarus_text_value_get(IkarusTextValue * value, size_t idx) { - return ikarus_value_base_get(value, idx)->data(); +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_get_size(IkarusTextValue const * value) { - return ikarus_value_base_get_size(value); +size_t ikarus_text_value_data_get_size(IkarusTextValue const * value) { + return ikarus_value_data_base_get_size(value); } -void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data) { - return ikarus_value_base_set(value, idx, new_data); +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_remove(IkarusTextValue * value, size_t idx) { - return ikarus_value_base_remove(value, idx); +void ikarus_text_value_data_remove(IkarusTextValue * value, size_t idx) { + return ikarus_value_data_base_remove(value, idx); } -void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data) { - return ikarus_value_base_insert(value, idx, new_data); +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_clear(IkarusTextValue * value) { - return ikarus_value_base_clear(value); +void ikarus_text_value_data_clear(IkarusTextValue * value) { + return ikarus_value_data_base_clear(value); } -bool ikarus_text_value_is_undefined(IkarusTextValue const * value) { - return ikarus_value_base_is_undefined(value); +bool ikarus_text_value_data_is_undefined(IkarusTextValue const * value) { + return ikarus_value_data_base_is_undefined(value); } -void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined) { - return ikarus_value_base_set_undefined(value, undefined); +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_to_string(IkarusTextValue const * value) { - return ikarus_value_base_to_string(value, [](auto const & value) { return value; }); +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_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs) { - return ikarus_value_base_is_equal(lhs, rhs); +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_copy(IkarusTextValue const * value) { - return ikarus_value_base_copy(value); +IkarusTextValue * ikarus_text_value_data_copy(IkarusTextValue const * value) { + return ikarus_value_data_base_copy(value); } -struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value) { - return ikarus_value_base_to_value(value); +struct IkarusValueData * ikarus_text_value_data_to_value(IkarusTextValue * value) { + return ikarus_value_data_base_to_value(value); } -struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value) { - return ikarus_value_base_to_value_const(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); } diff --git a/src/ikarus/values/text_value.hpp b/src/ikarus/values/text_value.hpp index 512bb8d..a1e36ea 100644 --- a/src/ikarus/values/text_value.hpp +++ b/src/ikarus/values/text_value.hpp @@ -4,7 +4,7 @@ #include -struct IkarusTextValue : IkarusValue { +struct IkarusTextValue : IkarusValueData { public: using DataType = std::string; diff --git a/src/ikarus/values/toggle_value.cpp b/src/ikarus/values/toggle_value.cpp index 7bec1f2..fb76e68 100644 --- a/src/ikarus/values/toggle_value.cpp +++ b/src/ikarus/values/toggle_value.cpp @@ -7,60 +7,60 @@ #include IkarusToggleValue::IkarusToggleValue(): - IkarusValue{this} {} + IkarusValueData{this} {} -IkarusToggleValue * ikarus_toggle_value_create() { +IkarusToggleValue * ikarus_toggle_value_data_create() { return new IkarusToggleValue{}; } -bool const * ikarus_toggle_value_get(IkarusToggleValue * value, size_t idx) { - return ikarus_value_base_get(value, idx); +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_get_size(IkarusToggleValue const * value) { - return ikarus_value_base_get_size(value); +size_t ikarus_toggle_value_data_get_size(IkarusToggleValue const * value) { + return ikarus_value_data_base_get_size(value); } -void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data) { - return ikarus_value_base_set(value, idx, new_data); +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_remove(IkarusToggleValue * value, size_t idx) { - return ikarus_value_base_remove(value, idx); +void ikarus_toggle_value_data_remove(IkarusToggleValue * value, size_t idx) { + return ikarus_value_data_base_remove(value, idx); } -void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data) { - return ikarus_value_base_insert(value, idx, new_data); +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_clear(IkarusToggleValue * value) { - return ikarus_value_base_clear(value); +void ikarus_toggle_value_data_clear(IkarusToggleValue * value) { + return ikarus_value_data_base_clear(value); } -bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value) { - return ikarus_value_base_is_undefined(value); +bool ikarus_toggle_value_data_is_undefined(IkarusToggleValue const * value) { + return ikarus_value_data_base_is_undefined(value); } -void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined) { - return ikarus_value_base_set_undefined(value, undefined); +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_to_string(IkarusToggleValue const * value) { - return ikarus_value_base_to_string(value, [](auto const & value) { return value ? "✓" : "✗"; }); +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_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs) { - return ikarus_value_base_is_equal(lhs, rhs); +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_copy(IkarusToggleValue const * value) { - return ikarus_value_base_copy(value); +IkarusToggleValue * ikarus_toggle_value_data_copy(IkarusToggleValue const * value) { + return ikarus_value_data_base_copy(value); } -struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value) { - return ikarus_value_base_to_value(value); +struct IkarusValueData * ikarus_toggle_value_data_to_value(IkarusToggleValue * value) { + return ikarus_value_data_base_to_value(value); } -struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value) { - return ikarus_value_base_to_value_const(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); } diff --git a/src/ikarus/values/toggle_value.hpp b/src/ikarus/values/toggle_value.hpp index 6882ecc..fade2c1 100644 --- a/src/ikarus/values/toggle_value.hpp +++ b/src/ikarus/values/toggle_value.hpp @@ -4,7 +4,7 @@ #include -struct IkarusToggleValue : IkarusValue { +struct IkarusToggleValue : IkarusValueData { public: using DataType = bool; diff --git a/src/ikarus/values/value.cpp b/src/ikarus/values/value.cpp index bbea7f3..de57e26 100644 --- a/src/ikarus/values/value.cpp +++ b/src/ikarus/values/value.cpp @@ -3,41 +3,53 @@ #include #include - -// required for header-only inclusion -#include "cppbase/templates.hpp" - #include -#include +// required for header-only inclusion +#include + #include #include #include #include +#include +#include -IkarusValue::IkarusValue(Data data): - data(data) {} +IkarusValue::IkarusValue(Data data, IkarusValueCardinality cardinality): + data{data}, + cardinality{cardinality} {} cppbase::Result IkarusValue::from_json(boost::json::value json) { if (auto const * obj = json.if_object(); obj == nullptr) { return cppbase::err(FromJsonError{}); } else { - boost::int64_t const * type = nullptr; - boost::json::value const * data = nullptr; + int64_t const * type; + int64_t const * cardinality; + boost::json::value const * data; - if (auto const * type_value = obj->if_contains("type"); type_value == nullptr) { + 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 (data = obj->if_contains("data"); data == nullptr) { + 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{}); } - auto create_value = [data]() -> cppbase::Result { + if (data = obj->if_contains(IKARUS_VALUE_JSON_DATA_KEY); data == nullptr) { + return cppbase::err(FromJsonError{}); + } + + auto create_value = [data, cardinality]() -> cppbase::Result { T * ret = nullptr; + ret->cardinality = *cardinality; + if (data->is_null()) { ret = new T{}; ret->data = boost::variant2::monostate{}; @@ -58,13 +70,13 @@ cppbase::Result IkarusValue::from_jso }; switch (*type) { - case IkarusPropertyType_Toggle: { + case IkarusValueType_Toggle: { return create_value.operator()(); } - case IkarusPropertyType_Number: { + case IkarusValueType_Number: { return create_value.operator()(); } - case IkarusPropertyType_Text: { + case IkarusValueType_Text: { return create_value.operator()(); } default: { @@ -75,18 +87,18 @@ cppbase::Result IkarusValue::from_jso } boost::json::value IkarusValue::to_json() const { - auto type = boost::variant2::visit( + auto type = std::visit( cppbase::overloaded{ - []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusPropertyType_Toggle; }, - []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusPropertyType_Number; }, - []([[maybe_unused]] IkarusTextValue const * value) { return IkarusPropertyType_Text; } + []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusValueType_Toggle; }, + []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusValueType_Number; }, + []([[maybe_unused]] IkarusTextValue const * value) { return IkarusValueType_Text; } }, data ); - auto data_json = boost::variant2::visit( + auto data_json = std::visit( [](T const * value) -> boost::json::value { - return boost::variant2::visit( + 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); } @@ -98,8 +110,9 @@ boost::json::value IkarusValue::to_json() const { ); return { - {"type", type}, - {"data", data_json} + { IKARUS_VALUE_JSON_TYPE_KEY, type}, + {IKARUS_VALUE_JSON_CARDINALITY_KEY, cardinality}, + { IKARUS_VALUE_JSON_DATA_KEY, data_json} }; } @@ -110,7 +123,7 @@ void ikarus_value_visit( void (*text_visitor)(IkarusTextValue *, void *), void * data ) { - boost::variant2::visit( + std::visit( cppbase::overloaded{ [toggle_visitor, data](IkarusToggleValue * toggle_value) { toggle_visitor(toggle_value, data); }, [number_visitor, data](IkarusNumberValue * number_value) { number_visitor(number_value, data); }, @@ -119,20 +132,3 @@ void ikarus_value_visit( value->data ); } - -void ikarus_value_visit_const( - IkarusValue const * value, - void (*toggle_visitor)(IkarusToggleValue const *, void *), - void (*number_visitor)(IkarusNumberValue const *, void *), - void (*text_visitor)(IkarusTextValue const *, void *), - void * data -) { - boost::variant2::visit( - cppbase::overloaded{ - [toggle_visitor, data](IkarusToggleValue const * toggle_value) { toggle_visitor(toggle_value, data); }, - [number_visitor, data](IkarusNumberValue const * number_value) { number_visitor(number_value, data); }, - [text_visitor, data](IkarusTextValue const * text_value) { text_visitor(text_value, data); } - }, - value->data - ); -} diff --git a/src/ikarus/values/value.hpp b/src/ikarus/values/value.hpp index 1e5791b..07e92fc 100644 --- a/src/ikarus/values/value.hpp +++ b/src/ikarus/values/value.hpp @@ -1,20 +1,24 @@ #pragma once #include -#include #include #include #include +#include + +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 { public: - using Data = boost::variant2::variant; + using Data = std::variant; constexpr static auto SMALL_VEC_VALUE_SIZE = 8; public: - explicit IkarusValue(Data data); + explicit IkarusValue(Data data, IkarusValueCardinality cardinality); IkarusValue(IkarusValue const &) = default; IkarusValue(IkarusValue &&) noexcept = default; @@ -32,6 +36,7 @@ public: public: Data data; + IkarusValueCardinality cardinality; }; template<> @@ -74,3 +79,20 @@ IkarusValue * fetch_value_from_db(IkarusProject * project, IkarusErrorData * err 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( \ + "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); diff --git a/src/ikarus/values/value_base.hpp b/src/ikarus/values/value_base.hpp index d4e6e25..5fa18ae 100644 --- a/src/ikarus/values/value_base.hpp +++ b/src/ikarus/values/value_base.hpp @@ -7,9 +7,10 @@ #include template -typename V::DataType const * ikarus_value_base_get(V * value, size_t idx) { - if (auto * data = - boost::variant2::get_if>(&value->data); +typename V::DataType const * ikarus_value_data_base_get(V * value, size_t idx) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { return &(*data)[idx]; } @@ -18,9 +19,10 @@ typename V::DataType const * ikarus_value_base_get(V * value, size_t idx) { } template -size_t ikarus_value_base_get_size(V const * value) { - if (auto * data = - boost::variant2::get_if>(&value->data); +size_t ikarus_value_data_base_get_size(V const * value) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { return data->size(); } @@ -29,57 +31,61 @@ size_t ikarus_value_base_get_size(V const * value) { } template -void ikarus_value_base_set(V * value, size_t idx, typename V::DataType new_data) { - if (auto * data = - boost::variant2::get_if>(&value->data); +void ikarus_value_data_base_set(V * value, size_t idx, typename V::DataType new_data) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { (*data)[idx] = new_data; } } template -void ikarus_value_base_remove(V * value, size_t idx) { - if (auto * data = - boost::variant2::get_if>(&value->data); +void ikarus_value_data_base_remove(V * value, size_t idx) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { data->erase(data->begin() + idx); } } template -void ikarus_value_base_insert(V * value, size_t idx, typename V::DataType new_data) { - if (auto * data = - boost::variant2::get_if>(&value->data); +void ikarus_value_data_base_insert(V * value, size_t idx, typename V::DataType new_data) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { data->insert(data->begin() + idx, new_data); } } template -void ikarus_value_base_clear(V * value) { - if (auto * data = - boost::variant2::get_if>(&value->data); +void ikarus_value_data_base_clear(V * value) { + if (auto * data = boost::variant2::get_if>( + &value->data + ); data != nullptr) { data->clear(); } } template -bool ikarus_value_base_is_undefined(V const * value) { +bool ikarus_value_data_base_is_undefined(V const * value) { return boost::variant2::holds_alternative(value->data); } template -void ikarus_value_base_set_undefined(V * value, bool undefined) { +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{}; + value->data = boost::container::small_vector{}; } } template F> -char const * ikarus_value_base_to_string(V const * value, F transformer) { +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; }, @@ -100,21 +106,21 @@ char const * ikarus_value_base_to_string(V const * value, F transformer) { } template -bool ikarus_value_base_is_equal(V const * lhs, V const * rhs) { +bool ikarus_value_data_base_is_equal(V const * lhs, V const * rhs) { return lhs->data == rhs->data; } template -V * ikarus_value_base_copy(V const * value) { +V * ikarus_value_data_base_copy(V const * value) { return new V{*value}; } template -struct IkarusValue * ikarus_value_base_to_value(V * value) { - return static_cast(value); +struct IkarusValueData * ikarus_value_data_base_to_value(V * value) { + return static_cast(value); } template -struct IkarusValue const * ikarus_value_base_to_value_const(V const * value) { - return static_cast(value); +struct IkarusValueData const * ikarus_value_data_base_to_value_data_const(V const * value) { + return static_cast(value); } From 195f51d3d064618feda15eff0b7d4a65cf15909b Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 1 Jan 2025 13:49:05 +0100 Subject: [PATCH 152/166] finalize schema/data setup Signed-off-by: Folling --- .clang-format | 6 +- .gitmodules | 3 + CMakeLists.txt | 27 +- CMakePresets.json | 35 + LICENSE.md | 2 +- docs/Flags.md | 5 + docs/Ownership.md | 7 + docs/{ => doxygen}/DoxyFile | 0 docs/{ => doxygen}/format_fix.js | 0 docs/{ => doxygen}/header.html | 0 docs/internal/Caching.md | 2 + include/CMakeLists.txt | 2 - include/ikarus/CMakeLists.txt | 1 - include/ikarus/errors.h | 148 +- include/ikarus/models/CMakeLists.txt | 41 - include/ikarus/models/blueprint.fbs | 12 - include/ikarus/models/blueprint_generated.h | 153 -- include/ikarus/models/entity.fbs | 26 - include/ikarus/models/entity_generated.h | 398 ---- include/ikarus/models/property.fbs | 14 - include/ikarus/models/property_generated.h | 289 --- include/ikarus/models/value.fbs | 85 - include/ikarus/models/value_generated.h | 1719 ----------------- include/ikarus/objects/blueprint.h | 197 +- include/ikarus/objects/entity.h | 413 ++-- .../objects/properties/number_property.h | 40 - include/ikarus/objects/properties/property.h | 144 -- .../ikarus/objects/properties/text_property.h | 40 - .../objects/properties/toggle_property.h | 37 - include/ikarus/objects/property.h | 143 ++ include/ikarus/persistence/project.h | 201 +- include/ikarus/stdtypes.h | 9 +- include/ikarus/values/data.h | 48 + include/ikarus/values/number_value.h | 109 -- include/ikarus/values/schema.h | 58 + include/ikarus/values/text_value.h | 109 -- include/ikarus/values/toggle_value.h | 109 -- include/ikarus/values/value.h | 69 +- include/ikarus/values/value_cardinality.h | 23 - include/ikarus/values/value_type.h | 198 -- include/module.modulemap | 6 - src/CMakeLists.txt | 2 +- src/ikarus/errors.cpp | 81 +- src/ikarus/errors.hpp | 205 +- src/ikarus/objects/blueprint.cpp | 163 -- src/ikarus/objects/blueprint.hpp | 28 +- src/ikarus/objects/entity.cpp | 605 ++---- src/ikarus/objects/entity.hpp | 27 +- src/ikarus/objects/object.cpp | 7 - src/ikarus/objects/object.hpp | 36 - .../objects/properties/number_property.cpp | 32 - .../objects/properties/number_property.hpp | 14 - src/ikarus/objects/properties/property.cpp | 143 -- src/ikarus/objects/properties/property.hpp | 29 - .../objects/properties/text_property.cpp | 32 - .../objects/properties/text_property.hpp | 14 - .../objects/properties/toggle_property.cpp | 32 - .../objects/properties/toggle_property.hpp | 17 - src/ikarus/objects/properties/util.hpp | 95 - src/ikarus/objects/property.cpp | 15 + src/ikarus/objects/property.hpp | 23 + src/ikarus/objects/util.hpp | 63 - src/ikarus/persistence/migrations.hpp | 13 +- .../migrations/m0_initial_layout.sql | 18 +- src/ikarus/persistence/project.cpp | 603 +++--- src/ikarus/persistence/project.hpp | 55 +- src/ikarus/values/data.cpp | 217 +++ src/ikarus/values/data.hpp | 65 + src/ikarus/values/entity_property_value.cpp | 21 - src/ikarus/values/entity_property_value.hpp | 1 - src/ikarus/values/errors.hpp | 36 + src/ikarus/values/number_value.cpp | 65 - src/ikarus/values/number_value.hpp | 25 - src/ikarus/values/schema.cpp | 177 ++ src/ikarus/values/schema.hpp | 51 + src/ikarus/values/shared.hpp | 65 + src/ikarus/values/text_value.cpp | 66 - src/ikarus/values/text_value.hpp | 24 - src/ikarus/values/toggle_value.cpp | 66 - src/ikarus/values/toggle_value.hpp | 24 - src/ikarus/values/value.cpp | 170 +- src/ikarus/values/value.hpp | 102 +- src/ikarus/values/value_base.hpp | 126 -- tests/CMakeLists.txt | 0 tools/cmake/toolchains/mac.cmake | 5 + vendor/CMakeLists.txt | 5 + vendor/catch2 | 1 + vendor/json | 1 + vendor/sqlitecpp | 2 +- 89 files changed, 2324 insertions(+), 6271 deletions(-) create mode 100644 CMakePresets.json create mode 100644 docs/Flags.md create mode 100644 docs/Ownership.md rename docs/{ => doxygen}/DoxyFile (100%) rename docs/{ => doxygen}/format_fix.js (100%) rename docs/{ => doxygen}/header.html (100%) create mode 100644 docs/internal/Caching.md delete mode 100644 include/ikarus/CMakeLists.txt delete mode 100644 include/ikarus/models/CMakeLists.txt delete mode 100644 include/ikarus/models/blueprint.fbs delete mode 100644 include/ikarus/models/blueprint_generated.h delete mode 100644 include/ikarus/models/entity.fbs delete mode 100644 include/ikarus/models/entity_generated.h delete mode 100644 include/ikarus/models/property.fbs delete mode 100644 include/ikarus/models/property_generated.h delete mode 100644 include/ikarus/models/value.fbs delete mode 100644 include/ikarus/models/value_generated.h delete mode 100644 include/ikarus/objects/properties/number_property.h delete mode 100644 include/ikarus/objects/properties/property.h delete mode 100644 include/ikarus/objects/properties/text_property.h delete mode 100644 include/ikarus/objects/properties/toggle_property.h create mode 100644 include/ikarus/objects/property.h create mode 100644 include/ikarus/values/data.h delete mode 100644 include/ikarus/values/number_value.h create mode 100644 include/ikarus/values/schema.h delete mode 100644 include/ikarus/values/text_value.h delete mode 100644 include/ikarus/values/toggle_value.h delete mode 100644 include/ikarus/values/value_cardinality.h delete mode 100644 include/ikarus/values/value_type.h delete mode 100644 include/module.modulemap delete mode 100644 src/ikarus/objects/object.cpp delete mode 100644 src/ikarus/objects/object.hpp delete mode 100644 src/ikarus/objects/properties/number_property.cpp delete mode 100644 src/ikarus/objects/properties/number_property.hpp delete mode 100644 src/ikarus/objects/properties/property.cpp delete mode 100644 src/ikarus/objects/properties/property.hpp delete mode 100644 src/ikarus/objects/properties/text_property.cpp delete mode 100644 src/ikarus/objects/properties/text_property.hpp delete mode 100644 src/ikarus/objects/properties/toggle_property.cpp delete mode 100644 src/ikarus/objects/properties/toggle_property.hpp delete mode 100644 src/ikarus/objects/properties/util.hpp create mode 100644 src/ikarus/objects/property.cpp create mode 100644 src/ikarus/objects/property.hpp delete mode 100644 src/ikarus/objects/util.hpp create mode 100644 src/ikarus/values/data.cpp create mode 100644 src/ikarus/values/data.hpp delete mode 100644 src/ikarus/values/entity_property_value.cpp delete mode 100644 src/ikarus/values/entity_property_value.hpp create mode 100644 src/ikarus/values/errors.hpp delete mode 100644 src/ikarus/values/number_value.cpp delete mode 100644 src/ikarus/values/number_value.hpp create mode 100644 src/ikarus/values/schema.cpp create mode 100644 src/ikarus/values/schema.hpp create mode 100644 src/ikarus/values/shared.hpp delete mode 100644 src/ikarus/values/text_value.cpp delete mode 100644 src/ikarus/values/text_value.hpp delete mode 100644 src/ikarus/values/toggle_value.cpp delete mode 100644 src/ikarus/values/toggle_value.hpp delete mode 100644 src/ikarus/values/value_base.hpp create mode 100644 tests/CMakeLists.txt create mode 100644 tools/cmake/toolchains/mac.cmake create mode 160000 vendor/catch2 create mode 160000 vendor/json diff --git a/.clang-format b/.clang-format index 85337cc..6d3560a 100644 --- a/.clang-format +++ b/.clang-format @@ -109,7 +109,9 @@ IncludeCategories: Priority: 11 - Regex: '^$' Priority: 12 - - Regex: '^$' + - Regex: '^$' + Priority: 13 + - Regex: '^$' Priority: 13 IndentAccessModifiers: false @@ -189,4 +191,4 @@ SpacesInSquareBrackets: false Standard: c++20 TabWidth: 4 -UseTab: Never +UseTab: Always diff --git a/.gitmodules b/.gitmodules index 933638b..94e3d41 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "vendor/cppbase"] path = vendor/cppbase url = ssh://git@git.rewritesarebliss.com:16658/Folling/cppbase.git +[submodule "vendor/json"] + path = vendor/json + url = git@github.com:nlohmann/json.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a16b08..63b949c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.31) project(ikarus) option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF) @@ -9,7 +9,7 @@ add_subdirectory(vendor) add_subdirectory(include) add_subdirectory(src) -find_package(Boost COMPONENTS system filesystem REQUIRED) +find_package(Boost CONFIG COMPONENTS system filesystem REQUIRED) add_library( ikarus SHARED @@ -17,40 +17,31 @@ add_library( ${SOURCE_FILES} ) -add_dependencies( - ikarus - flatbuffer_headers -) - set_target_properties( ikarus PROPERTIES CXX_STANDARD 23 CXX_STANDARD_REQUIRED ON + LINKER_LANGUAGE CXX POSITION_INDEPENDENT_CODE TRUE ) target_include_directories( - ikarus PUBLIC + ikarus + PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include -) - -target_include_directories( - ikarus PRIVATE + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src + ${Boost_INCLUDE_DIR} ) target_link_libraries( ikarus PRIVATE cppbase sqlitecpp + nlohmann_json::nlohmann_json ${Boost_LIBRARIES} ) -target_include_directories( - ikarus PRIVATE - ${Boost_INCLUDE_DIR} -) - if (LIBIKARUS_ENABLE_LINTS) find_program(IWYU_PATH NAMES include-what-you-use iwyu 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) add_custom_target( libikarus_docs - WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/docs + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/docs/doxygen COMMAND ${DOXYGEN_PATH} DoxyFile COMMENT "Generating documentation with Doxygen" VERBATIM diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..0772ba0 --- /dev/null +++ b/CMakePresets.json @@ -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" + } + ] +} diff --git a/LICENSE.md b/LICENSE.md index 5f2c6f6..41924e1 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -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 following conditions are met: diff --git a/docs/Flags.md b/docs/Flags.md new file mode 100644 index 0000000..6f54cdc --- /dev/null +++ b/docs/Flags.md @@ -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. \ No newline at end of file diff --git a/docs/Ownership.md b/docs/Ownership.md new file mode 100644 index 0000000..9827299 --- /dev/null +++ b/docs/Ownership.md @@ -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. \ No newline at end of file diff --git a/docs/DoxyFile b/docs/doxygen/DoxyFile similarity index 100% rename from docs/DoxyFile rename to docs/doxygen/DoxyFile diff --git a/docs/format_fix.js b/docs/doxygen/format_fix.js similarity index 100% rename from docs/format_fix.js rename to docs/doxygen/format_fix.js diff --git a/docs/header.html b/docs/doxygen/header.html similarity index 100% rename from docs/header.html rename to docs/doxygen/header.html diff --git a/docs/internal/Caching.md b/docs/internal/Caching.md new file mode 100644 index 0000000..cd4e262 --- /dev/null +++ b/docs/internal/Caching.md @@ -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. diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index e3043c7..d5a0a7a 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -5,5 +5,3 @@ file( ) set(INCLUDE_FILES ${FILES} PARENT_SCOPE) - -add_subdirectory(ikarus) \ No newline at end of file diff --git a/include/ikarus/CMakeLists.txt b/include/ikarus/CMakeLists.txt deleted file mode 100644 index cd981b8..0000000 --- a/include/ikarus/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(models) \ No newline at end of file diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index 926f07d..a41c291 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -1,19 +1,17 @@ #pragma once /// \file errors.h -/// \author Folling +/// \author Folling #include /// \addtogroup errors Errors /// \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. -/// Upon erring the function will store relevant information about the error in the out parameter. -/// If the out parameter is null nothing will be stored. This is not recommended as it essentially ignores errors. -/// For the sake of simplicity we have avoided mechanisms that "force" clients to handle errors. -/// Note that Ikarus does not check for null pointers. Passing null pointers to functions that do not explicitly state that they accept null -/// pointers is undefined behaviour. This decision is done for the sake of brevity and readability. `project_get_name(project)` is also -/// synonymous to `project->get_name()` in OOP languages, which shares the same semantics. +/// Upon erring the function will store relevant information about the error in +/// the out parameter. If the out parameter is null nothing will be stored. This +/// is not recommended as it essentially ignores errors. For the sake of +/// simplicity we have avoided mechanisms that "force" clients to handle errors. /// @{ 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 /// fault of libikarus, it just indicates where the error occurred. enum IkarusErrorInfo { - /// \brief No error occurred. - IkarusErrorInfo_None = 0x0, - /// \brief The client misused the API. - /// Example: Accessing a resource that does not exist. - IkarusErrorInfo_Client_Misuse = 0x01000001, - /// \brief The client provided a null value for a parameter that must not be null. - /// Example: Passing null for `ikarus_project_get_name` - IkarusErrorInfo_Client_InvalidNull = 0x01000002, - /// \brief The client provided an index that was out of bounds for some array. - /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3. - IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000003, - /// \brief The client provided a numeric value that was out of bounds - /// Example: Passing the value 2^32 to an i32 (might be passed as a string). - IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000004, - /// \brief The client provided invalid input that doesn't fit in any of the other categories. - /// Example: Passing an empty/blank string for a string that must be non-empty/-blank. - IkarusErrorInfo_Client_InvalidInput = 0x01000005, - /// \brief The client provided valid data in an invalid format. - /// Example: Passing a malformed JSON string. - IkarusErrorInfo_Client_InvalidFormat = 0x01000006, - /// \brief The client violated a constraint. - /// \details This error is most likely caused by endusers. - /// Example: A user tries to set the age of a character to an value outside of their specified range. - IkarusErrorInfo_Client_ConstraintViolated = 0x10000007, + /// \brief No error occurred. + IkarusErrorInfo_None = 0x0, + /// \brief The client misused the API. + /// Example: Accessing a resource that does not exist. + IkarusErrorInfo_Client_Misuse = 0x01000001, + /// \brief The client provided a null value for a parameter that must not be null. + /// Example: Passing null for `ikarus_project_get_name` + IkarusErrorInfo_Client_InvalidNull = 0x01000002, + /// \brief The client provided a non-existent resource. + /// Example: Passing an entity to a function after it has been deleted. + IkarusErrorInfo_Client_NonExistent = 0x01000003, + /// \brief The client provided an index that was out of bounds for some array. + /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3. + IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000004, + /// \brief The client provided a numeric value that was out of bounds + /// Example: Passing the value 2^32 to an i32 (might be passed as a string). + IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000005, + /// \brief The client provided invalid input that doesn't fit in any of the other categories. + /// Example: Passing an empty/blank string for a string that must be + /// non-empty/-blank. + IkarusErrorInfo_Client_InvalidInput = 0x01000006, + /// \brief The client provided valid data in an invalid format. + /// Example: Passing a malformed JSON string. + 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. - IkarusErrorInfo_Filesystem_AccessIssue = 0x03000001, - /// \brief A file was not found. - IkarusErrorInfo_Filesystem_NotFound = 0x03000002, - /// \brief A file or directory already exists. - IkarusErrorInfo_Filesystem_AlreadyExists = 0x03000003, - /// \brief Missing permissions to access a file or directory. - IkarusErrorInfo_Filesystem_MissingPermissions = 0x03000004, - /// \brief Insufficient space to perform an operation. - IkarusErrorInfo_Filesystem_InsufficientSpace = 0x03000005, - /// \brief A path is invalid. - IkarusErrorInfo_Filesystem_InvalidPath = 0x03000006, + /// \brief A file was not found. + IkarusErrorInfo_Filesystem_NotFound = 0x03000001, + /// \brief A file or directory already exists. + IkarusErrorInfo_Filesystem_AlreadyExists = 0x03000002, + /// \brief Missing permissions to access a file or directory. + IkarusErrorInfo_Filesystem_MissingPermissions = 0x03000003, + /// \brief Insufficient space to perform an operation. + IkarusErrorInfo_Filesystem_InsufficientSpace = 0x03000004, + /// \brief A path is invalid. + IkarusErrorInfo_Filesystem_InvalidPath = 0x03000005, - /// \brief A database connection failed. - IkarusErrorInfo_Database_ConnectionFailed = 0x04000001, - /// \brief A database query failed. - IkarusErrorInfo_Database_QueryFailed = 0x04000002, - /// \brief A database migration failed. - IkarusErrorInfo_Database_MigrationFailed = 0x04000003, - /// \brief A database is in an invalid state. This indicates a corrupt project. - /// Example: An entity is linked to a non-existant blueprint. - IkarusErrorInfo_Database_InvalidState = 0x04000004, + /// \brief A database connection failed. + IkarusErrorInfo_Database_ConnectionFailed = 0x04000001, + /// \brief A database query failed. + IkarusErrorInfo_Database_QueryFailed = 0x04000002, + /// \brief A database migration failed. + IkarusErrorInfo_Database_MigrationFailed = 0x04000003, + /// \brief A database is in an invalid state. This indicates a corrupt project. + /// Example: An entity is linked to a non-existent blueprint. + IkarusErrorInfo_Database_InvalidState = 0x04000004, - /// \brief A system call failed. - IkarusErrorInfo_OS_SystemCallFailed = 0x05000001, - /// \brief A system call returned an invalid value. - IkarusErrorInfo_OS_InvalidReturnValue = 0x05000002, - /// \brief An OOM error occurred. - IkarusErrorInfo_OS_InsufficientMemory = 0x05000003, + /// \brief A system call failed. + IkarusErrorInfo_OS_SystemCallFailed = 0x05000001, + /// \brief A system call returned an invalid value. + IkarusErrorInfo_OS_InvalidReturnValue = 0x05000002, + /// \brief An OOM error occurred. + IkarusErrorInfo_OS_InsufficientMemory = 0x05000003, - /// \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, - /// whereas the former may imply that the state is ephemeral, e.g. data within a function. - /// Example: The name of an object is found to be invalid UTF8. - IkarusErrorInfo_LibIkarus_InvalidState = 0x06000001, - /// \brief libikarus is unable to perform a certain operation that should succeed. - IkarusErrorInfo_LibIkarus_CannotPerformOperation = 0x06000002, - /// \brief libikarus is unable to perform a certain operation within a given timeframe. - /// Example: A query takes longer than the timeout. - IkarusErrorInfo_LibIkarus_Timeout = 0x06000003, + /// \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, + /// whereas the former may imply that the state is ephemeral, e.g. data + /// within a function. + /// Example: The name of an object is found to be invalid UTF8. + IkarusErrorInfo_LibIkarus_InvalidState = 0x06000001, + /// \brief libikarus is unable to perform a certain operation that should succeed. + IkarusErrorInfo_LibIkarus_CannotPerformOperation = 0x06000002, + /// \brief libikarus is unable to perform a certain operation within a given timeframe. + /// Example: A query takes longer than the timeout. + IkarusErrorInfo_LibIkarus_Timeout = 0x06000003, }; /// \brief The maximum length of an error message. @@ -96,17 +98,17 @@ enum IkarusErrorInfo { /// \brief The data stored for an error struct IkarusErrorData { - /// \brief The error type - IkarusErrorInfo info; + /// \brief The error type + 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. /// \param info The error info to get the name of. /// \return The name of the error info. /// \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. /// \param data The error data to check. diff --git a/include/ikarus/models/CMakeLists.txt b/include/ikarus/models/CMakeLists.txt deleted file mode 100644 index e3d8758..0000000 --- a/include/ikarus/models/CMakeLists.txt +++ /dev/null @@ -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 -) diff --git a/include/ikarus/models/blueprint.fbs b/include/ikarus/models/blueprint.fbs deleted file mode 100644 index fb782d8..0000000 --- a/include/ikarus/models/blueprint.fbs +++ /dev/null @@ -1,12 +0,0 @@ -include "property.fbs"; - -namespace Ikarus; - -table Blueprint { - id: int64; - name: string; - description: string; - tags: [string]; -} - -root_type Blueprint; diff --git a/include/ikarus/models/blueprint_generated.h b/include/ikarus/models/blueprint_generated.h deleted file mode 100644 index b70924b..0000000 --- a/include/ikarus/models/blueprint_generated.h +++ /dev/null @@ -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(VT_ID, 0); - } - const ::flatbuffers::String *name() const { - return GetPointer(VT_NAME); - } - const ::flatbuffers::String *description() const { - return GetPointer(VT_DESCRIPTION); - } - const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { - return GetPointer> *>(VT_TAGS); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(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(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 Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset 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 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(buf); -} - -inline const Ikarus::Blueprint *GetSizePrefixedBlueprint(const void *buf) { - return ::flatbuffers::GetSizePrefixedRoot(buf); -} - -inline bool VerifyBlueprintBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifyBuffer(nullptr); -} - -inline bool VerifySizePrefixedBlueprintBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifySizePrefixedBuffer(nullptr); -} - -inline void FinishBlueprintBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.Finish(root); -} - -inline void FinishSizePrefixedBlueprintBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.FinishSizePrefixed(root); -} - -} // namespace Ikarus - -#endif // FLATBUFFERS_GENERATED_BLUEPRINT_IKARUS_H_ diff --git a/include/ikarus/models/entity.fbs b/include/ikarus/models/entity.fbs deleted file mode 100644 index bce2ff5..0000000 --- a/include/ikarus/models/entity.fbs +++ /dev/null @@ -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; diff --git a/include/ikarus/models/entity_generated.h b/include/ikarus/models/entity_generated.h deleted file mode 100644 index e253038..0000000 --- a/include/ikarus/models/entity_generated.h +++ /dev/null @@ -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(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 - 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(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 value) { - fbb_.AddOffset(NamedValue::VT_VALUE, value); - } - explicit NamedValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - fbb_.Required(o, NamedValue::VT_NAME); - return o; - } -}; - -inline ::flatbuffers::Offset CreateNamedValue( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::String> name = 0, - ::flatbuffers::Offset 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 CreateNamedValueDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const char *name = nullptr, - ::flatbuffers::Offset 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(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(property_id() > _property_id) - static_cast(property_id() < _property_id); - } - Ikarus::Value::Data data_type() const { - return static_cast(GetField(VT_DATA_TYPE, 0)); - } - const void *data() const { - return GetPointer(VT_DATA); - } - template const T *data_as() const; - const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { - return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { - return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { - return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::SimpleData *data_as_SimpleData() const { - return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::CombinedData *data_as_CombinedData() const { - return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ListData *data_as_ListData() const { - return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ComplexData *data_as_ComplexData() const { - return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_PROPERTY_ID, 8) && - VerifyField(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() const { - return data_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *PropertyValue::data_as() const { - return data_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *PropertyValue::data_as() const { - return data_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *PropertyValue::data_as() const { - return data_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *PropertyValue::data_as() const { - return data_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *PropertyValue::data_as() const { - return data_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *PropertyValue::data_as() 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(PropertyValue::VT_PROPERTY_ID, property_id, 0); - } - void add_data_type(Ikarus::Value::Data data_type) { - fbb_.AddElement(PropertyValue::VT_DATA_TYPE, static_cast(data_type), 0); - } - void add_data(::flatbuffers::Offset data) { - fbb_.AddOffset(PropertyValue::VT_DATA, data); - } - explicit PropertyValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreatePropertyValue( - ::flatbuffers::FlatBufferBuilder &_fbb, - int64_t property_id = 0, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset 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(VT_ID, 0); - } - const ::flatbuffers::String *name() const { - return GetPointer(VT_NAME); - } - const ::flatbuffers::String *description() const { - return GetPointer(VT_DESCRIPTION); - } - const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { - return GetPointer> *>(VT_TAGS); - } - const ::flatbuffers::Vector<::flatbuffers::Offset> *values() const { - return GetPointer> *>(VT_VALUES); - } - const ::flatbuffers::Vector<::flatbuffers::Offset> *property_values() const { - return GetPointer> *>(VT_PROPERTY_VALUES); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(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(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>> values) { - fbb_.AddOffset(Entity::VT_VALUES, values); - } - void add_property_values(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> property_values) { - fbb_.AddOffset(Entity::VT_PROPERTY_VALUES, property_values); - } - explicit EntityBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset 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>> values = 0, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> 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 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> *values = nullptr, - std::vector<::flatbuffers::Offset> *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(values) : 0; - auto property_values__ = property_values ? _fbb.CreateVectorOfSortedTables(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(buf); -} - -inline const Ikarus::Entity *GetSizePrefixedEntity(const void *buf) { - return ::flatbuffers::GetSizePrefixedRoot(buf); -} - -inline bool VerifyEntityBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifyBuffer(nullptr); -} - -inline bool VerifySizePrefixedEntityBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifySizePrefixedBuffer(nullptr); -} - -inline void FinishEntityBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.Finish(root); -} - -inline void FinishSizePrefixedEntityBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.FinishSizePrefixed(root); -} - -} // namespace Ikarus - -#endif // FLATBUFFERS_GENERATED_ENTITY_IKARUS_H_ diff --git a/include/ikarus/models/property.fbs b/include/ikarus/models/property.fbs deleted file mode 100644 index 8a96bb2..0000000 --- a/include/ikarus/models/property.fbs +++ /dev/null @@ -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; diff --git a/include/ikarus/models/property_generated.h b/include/ikarus/models/property_generated.h deleted file mode 100644 index 1111bee..0000000 --- a/include/ikarus/models/property_generated.h +++ /dev/null @@ -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(VT_ID, 0); - } - const ::flatbuffers::String *name() const { - return GetPointer(VT_NAME); - } - const ::flatbuffers::String *description() const { - return GetPointer(VT_DESCRIPTION); - } - const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *tags() const { - return GetPointer> *>(VT_TAGS); - } - Ikarus::Value::Schema value_schema_type() const { - return static_cast(GetField(VT_VALUE_SCHEMA_TYPE, 0)); - } - const void *value_schema() const { - return GetPointer(VT_VALUE_SCHEMA); - } - template 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(value_schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *value_schema_as_SimpleSchema() const { - return value_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(value_schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *value_schema_as_CombinedSchema() const { - return value_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(value_schema()) : nullptr; - } - const Ikarus::Value::ListSchema *value_schema_as_ListSchema() const { - return value_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(value_schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *value_schema_as_ComplexSchema() const { - return value_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(value_schema()) : nullptr; - } - Ikarus::Value::Data default_value_type() const { - return static_cast(GetField(VT_DEFAULT_VALUE_TYPE, 0)); - } - const void *default_value() const { - return GetPointer(VT_DEFAULT_VALUE); - } - template 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(default_value()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *default_value_as_NumberDataPoint() const { - return default_value_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(default_value()) : nullptr; - } - const Ikarus::Value::TextDataPoint *default_value_as_TextDataPoint() const { - return default_value_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(default_value()) : nullptr; - } - const Ikarus::Value::SimpleData *default_value_as_SimpleData() const { - return default_value_type() == Ikarus::Value::Data::SimpleData ? static_cast(default_value()) : nullptr; - } - const Ikarus::Value::CombinedData *default_value_as_CombinedData() const { - return default_value_type() == Ikarus::Value::Data::CombinedData ? static_cast(default_value()) : nullptr; - } - const Ikarus::Value::ListData *default_value_as_ListData() const { - return default_value_type() == Ikarus::Value::Data::ListData ? static_cast(default_value()) : nullptr; - } - const Ikarus::Value::ComplexData *default_value_as_ComplexData() const { - return default_value_type() == Ikarus::Value::Data::ComplexData ? static_cast(default_value()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(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(verifier, VT_VALUE_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_VALUE_SCHEMA) && - VerifySchema(verifier, value_schema(), value_schema_type()) && - VerifyField(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() const { - return value_schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *Property::value_schema_as() const { - return value_schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *Property::value_schema_as() const { - return value_schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *Property::value_schema_as() const { - return value_schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *Property::value_schema_as() const { - return value_schema_as_ComplexSchema(); -} - -template<> inline const Ikarus::Value::ToggleDataPoint *Property::default_value_as() const { - return default_value_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *Property::default_value_as() const { - return default_value_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *Property::default_value_as() const { - return default_value_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *Property::default_value_as() const { - return default_value_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *Property::default_value_as() const { - return default_value_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *Property::default_value_as() const { - return default_value_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *Property::default_value_as() 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(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(Property::VT_VALUE_SCHEMA_TYPE, static_cast(value_schema_type), 0); - } - void add_value_schema(::flatbuffers::Offset value_schema) { - fbb_.AddOffset(Property::VT_VALUE_SCHEMA, value_schema); - } - void add_default_value_type(Ikarus::Value::Data default_value_type) { - fbb_.AddElement(Property::VT_DEFAULT_VALUE_TYPE, static_cast(default_value_type), 0); - } - void add_default_value(::flatbuffers::Offset default_value) { - fbb_.AddOffset(Property::VT_DEFAULT_VALUE, default_value); - } - explicit PropertyBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset 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 value_schema = 0, - Ikarus::Value::Data default_value_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset 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 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 value_schema = 0, - Ikarus::Value::Data default_value_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset 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(buf); -} - -inline const Ikarus::Property *GetSizePrefixedProperty(const void *buf) { - return ::flatbuffers::GetSizePrefixedRoot(buf); -} - -inline bool VerifyPropertyBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifyBuffer(nullptr); -} - -inline bool VerifySizePrefixedPropertyBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifySizePrefixedBuffer(nullptr); -} - -inline void FinishPropertyBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.Finish(root); -} - -inline void FinishSizePrefixedPropertyBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.FinishSizePrefixed(root); -} - -} // namespace Ikarus - -#endif // FLATBUFFERS_GENERATED_PROPERTY_IKARUS_H_ diff --git a/include/ikarus/models/value.fbs b/include/ikarus/models/value.fbs deleted file mode 100644 index 27a27e9..0000000 --- a/include/ikarus/models/value.fbs +++ /dev/null @@ -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; diff --git a/include/ikarus/models/value_generated.h b/include/ikarus/models/value_generated.h deleted file mode 100644 index c9da9e3..0000000 --- a/include/ikarus/models/value_generated.h +++ /dev/null @@ -1,1719 +0,0 @@ -// automatically generated by the FlatBuffers compiler, do not modify - - -#ifndef FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_H_ -#define FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_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"); - -namespace Ikarus { -namespace Value { - -struct ToggleDataPoint; -struct ToggleDataPointBuilder; - -struct NumberDataPoint; -struct NumberDataPointBuilder; - -struct TextDataPoint; -struct TextDataPointBuilder; - -struct ConstantSchema; -struct ConstantSchemaBuilder; - -struct SimpleSchema; -struct SimpleSchemaBuilder; - -struct SimpleData; -struct SimpleDataBuilder; - -struct CombinedSchema; -struct CombinedSchemaBuilder; - -struct CombinedData; -struct CombinedDataBuilder; - -struct ListSchema; -struct ListSchemaBuilder; - -struct ListData; -struct ListDataBuilder; - -struct NamedSchema; -struct NamedSchemaBuilder; - -struct ComplexSchema; -struct ComplexSchemaBuilder; - -struct NamedData; -struct NamedDataBuilder; - -struct ComplexData; -struct ComplexDataBuilder; - -struct Value; -struct ValueBuilder; - -enum class Data : uint8_t { - NONE = 0, - ToggleDataPoint = 1, - NumberDataPoint = 2, - TextDataPoint = 3, - SimpleData = 4, - CombinedData = 5, - ListData = 6, - ComplexData = 7, - MIN = NONE, - MAX = ComplexData -}; - -inline const Data (&EnumValuesData())[8] { - static const Data values[] = { - Data::NONE, - Data::ToggleDataPoint, - Data::NumberDataPoint, - Data::TextDataPoint, - Data::SimpleData, - Data::CombinedData, - Data::ListData, - Data::ComplexData - }; - return values; -} - -inline const char * const *EnumNamesData() { - static const char * const names[9] = { - "NONE", - "ToggleDataPoint", - "NumberDataPoint", - "TextDataPoint", - "SimpleData", - "CombinedData", - "ListData", - "ComplexData", - nullptr - }; - return names; -} - -inline const char *EnumNameData(Data e) { - if (::flatbuffers::IsOutRange(e, Data::NONE, Data::ComplexData)) return ""; - const size_t index = static_cast(e); - return EnumNamesData()[index]; -} - -template struct DataTraits { - static const Data enum_value = Data::NONE; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::ToggleDataPoint; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::NumberDataPoint; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::TextDataPoint; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::SimpleData; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::CombinedData; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::ListData; -}; - -template<> struct DataTraits { - static const Data enum_value = Data::ComplexData; -}; - -bool VerifyData(::flatbuffers::Verifier &verifier, const void *obj, Data type); -bool VerifyDataVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); - -enum class Schema : uint8_t { - NONE = 0, - ConstantSchema = 1, - SimpleSchema = 2, - CombinedSchema = 3, - ListSchema = 4, - ComplexSchema = 5, - MIN = NONE, - MAX = ComplexSchema -}; - -inline const Schema (&EnumValuesSchema())[6] { - static const Schema values[] = { - Schema::NONE, - Schema::ConstantSchema, - Schema::SimpleSchema, - Schema::CombinedSchema, - Schema::ListSchema, - Schema::ComplexSchema - }; - return values; -} - -inline const char * const *EnumNamesSchema() { - static const char * const names[7] = { - "NONE", - "ConstantSchema", - "SimpleSchema", - "CombinedSchema", - "ListSchema", - "ComplexSchema", - nullptr - }; - return names; -} - -inline const char *EnumNameSchema(Schema e) { - if (::flatbuffers::IsOutRange(e, Schema::NONE, Schema::ComplexSchema)) return ""; - const size_t index = static_cast(e); - return EnumNamesSchema()[index]; -} - -template struct SchemaTraits { - static const Schema enum_value = Schema::NONE; -}; - -template<> struct SchemaTraits { - static const Schema enum_value = Schema::ConstantSchema; -}; - -template<> struct SchemaTraits { - static const Schema enum_value = Schema::SimpleSchema; -}; - -template<> struct SchemaTraits { - static const Schema enum_value = Schema::CombinedSchema; -}; - -template<> struct SchemaTraits { - static const Schema enum_value = Schema::ListSchema; -}; - -template<> struct SchemaTraits { - static const Schema enum_value = Schema::ComplexSchema; -}; - -bool VerifySchema(::flatbuffers::Verifier &verifier, const void *obj, Schema type); -bool VerifySchemaVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); - -struct ToggleDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ToggleDataPointBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA = 4 - }; - const ::flatbuffers::Vector *data() const { - return GetPointer *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - verifier.EndTable(); - } -}; - -struct ToggleDataPointBuilder { - typedef ToggleDataPoint Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data(::flatbuffers::Offset<::flatbuffers::Vector> data) { - fbb_.AddOffset(ToggleDataPoint::VT_DATA, data); - } - explicit ToggleDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateToggleDataPoint( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector> data = 0) { - ToggleDataPointBuilder builder_(_fbb); - builder_.add_data(data); - return builder_.Finish(); -} - -struct ToggleDataPoint::Traits { - using type = ToggleDataPoint; - static auto constexpr Create = CreateToggleDataPoint; -}; - -inline ::flatbuffers::Offset CreateToggleDataPointDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *data = nullptr) { - auto data__ = data ? _fbb.CreateVector(*data) : 0; - return Ikarus::Value::CreateToggleDataPoint( - _fbb, - data__); -} - -struct NumberDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef NumberDataPointBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA = 4 - }; - const ::flatbuffers::Vector *data() const { - return GetPointer *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - verifier.EndTable(); - } -}; - -struct NumberDataPointBuilder { - typedef NumberDataPoint Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data(::flatbuffers::Offset<::flatbuffers::Vector> data) { - fbb_.AddOffset(NumberDataPoint::VT_DATA, data); - } - explicit NumberDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateNumberDataPoint( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector> data = 0) { - NumberDataPointBuilder builder_(_fbb); - builder_.add_data(data); - return builder_.Finish(); -} - -struct NumberDataPoint::Traits { - using type = NumberDataPoint; - static auto constexpr Create = CreateNumberDataPoint; -}; - -inline ::flatbuffers::Offset CreateNumberDataPointDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *data = nullptr) { - auto data__ = data ? _fbb.CreateVector(*data) : 0; - return Ikarus::Value::CreateNumberDataPoint( - _fbb, - data__); -} - -struct TextDataPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef TextDataPointBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA = 4 - }; - const ::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>> *data() const { - return GetPointer> *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - verifier.VerifyVectorOfStrings(data()) && - verifier.EndTable(); - } -}; - -struct TextDataPointBuilder { - typedef TextDataPoint Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> data) { - fbb_.AddOffset(TextDataPoint::VT_DATA, data); - } - explicit TextDataPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateTextDataPoint( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>> data = 0) { - TextDataPointBuilder builder_(_fbb); - builder_.add_data(data); - return builder_.Finish(); -} - -struct TextDataPoint::Traits { - using type = TextDataPoint; - static auto constexpr Create = CreateTextDataPoint; -}; - -inline ::flatbuffers::Offset CreateTextDataPointDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector<::flatbuffers::Offset<::flatbuffers::String>> *data = nullptr) { - auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::String>>(*data) : 0; - return Ikarus::Value::CreateTextDataPoint( - _fbb, - data__); -} - -struct ConstantSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ConstantSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SUB_SCHEMA_TYPE = 4, - VT_SUB_SCHEMA = 6, - VT_DATA_TYPE = 8, - VT_DATA = 10 - }; - Ikarus::Value::Schema sub_schema_type() const { - return static_cast(GetField(VT_SUB_SCHEMA_TYPE, 0)); - } - const void *sub_schema() const { - return GetPointer(VT_SUB_SCHEMA); - } - template const T *sub_schema_as() const; - const Ikarus::Value::ConstantSchema *sub_schema_as_ConstantSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *sub_schema_as_SimpleSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *sub_schema_as_CombinedSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::ListSchema *sub_schema_as_ListSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *sub_schema_as_ComplexSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(sub_schema()) : nullptr; - } - Ikarus::Value::Data data_type() const { - return static_cast(GetField(VT_DATA_TYPE, 0)); - } - const void *data() const { - return GetPointer(VT_DATA); - } - template const T *data_as() const; - const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { - return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { - return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { - return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::SimpleData *data_as_SimpleData() const { - return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::CombinedData *data_as_CombinedData() const { - return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ListData *data_as_ListData() const { - return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ComplexData *data_as_ComplexData() const { - return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_SUB_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_SUB_SCHEMA) && - VerifySchema(verifier, sub_schema(), sub_schema_type()) && - VerifyField(verifier, VT_DATA_TYPE, 1) && - VerifyOffset(verifier, VT_DATA) && - VerifyData(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ConstantSchema *ConstantSchema::sub_schema_as() const { - return sub_schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *ConstantSchema::sub_schema_as() const { - return sub_schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *ConstantSchema::sub_schema_as() const { - return sub_schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *ConstantSchema::sub_schema_as() const { - return sub_schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *ConstantSchema::sub_schema_as() const { - return sub_schema_as_ComplexSchema(); -} - -template<> inline const Ikarus::Value::ToggleDataPoint *ConstantSchema::data_as() const { - return data_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *ConstantSchema::data_as() const { - return data_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *ConstantSchema::data_as() const { - return data_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *ConstantSchema::data_as() const { - return data_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *ConstantSchema::data_as() const { - return data_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *ConstantSchema::data_as() const { - return data_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *ConstantSchema::data_as() const { - return data_as_ComplexData(); -} - -struct ConstantSchemaBuilder { - typedef ConstantSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_sub_schema_type(Ikarus::Value::Schema sub_schema_type) { - fbb_.AddElement(ConstantSchema::VT_SUB_SCHEMA_TYPE, static_cast(sub_schema_type), 0); - } - void add_sub_schema(::flatbuffers::Offset sub_schema) { - fbb_.AddOffset(ConstantSchema::VT_SUB_SCHEMA, sub_schema); - } - void add_data_type(Ikarus::Value::Data data_type) { - fbb_.AddElement(ConstantSchema::VT_DATA_TYPE, static_cast(data_type), 0); - } - void add_data(::flatbuffers::Offset data) { - fbb_.AddOffset(ConstantSchema::VT_DATA, data); - } - explicit ConstantSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateConstantSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - Ikarus::Value::Schema sub_schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset sub_schema = 0, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset data = 0) { - ConstantSchemaBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_sub_schema(sub_schema); - builder_.add_data_type(data_type); - builder_.add_sub_schema_type(sub_schema_type); - return builder_.Finish(); -} - -struct ConstantSchema::Traits { - using type = ConstantSchema; - static auto constexpr Create = CreateConstantSchema; -}; - -struct SimpleSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef SimpleSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SUB_SCHEMA_TYPE = 4, - VT_SUB_SCHEMA = 6 - }; - Ikarus::Value::Schema sub_schema_type() const { - return static_cast(GetField(VT_SUB_SCHEMA_TYPE, 0)); - } - const void *sub_schema() const { - return GetPointer(VT_SUB_SCHEMA); - } - template const T *sub_schema_as() const; - const Ikarus::Value::ConstantSchema *sub_schema_as_ConstantSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *sub_schema_as_SimpleSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *sub_schema_as_CombinedSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::ListSchema *sub_schema_as_ListSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(sub_schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *sub_schema_as_ComplexSchema() const { - return sub_schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(sub_schema()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_SUB_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_SUB_SCHEMA) && - VerifySchema(verifier, sub_schema(), sub_schema_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ConstantSchema *SimpleSchema::sub_schema_as() const { - return sub_schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *SimpleSchema::sub_schema_as() const { - return sub_schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *SimpleSchema::sub_schema_as() const { - return sub_schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *SimpleSchema::sub_schema_as() const { - return sub_schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *SimpleSchema::sub_schema_as() const { - return sub_schema_as_ComplexSchema(); -} - -struct SimpleSchemaBuilder { - typedef SimpleSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_sub_schema_type(Ikarus::Value::Schema sub_schema_type) { - fbb_.AddElement(SimpleSchema::VT_SUB_SCHEMA_TYPE, static_cast(sub_schema_type), 0); - } - void add_sub_schema(::flatbuffers::Offset sub_schema) { - fbb_.AddOffset(SimpleSchema::VT_SUB_SCHEMA, sub_schema); - } - explicit SimpleSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateSimpleSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - Ikarus::Value::Schema sub_schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset sub_schema = 0) { - SimpleSchemaBuilder builder_(_fbb); - builder_.add_sub_schema(sub_schema); - builder_.add_sub_schema_type(sub_schema_type); - return builder_.Finish(); -} - -struct SimpleSchema::Traits { - using type = SimpleSchema; - static auto constexpr Create = CreateSimpleSchema; -}; - -struct SimpleData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef SimpleDataBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA_TYPE = 4, - VT_DATA = 6 - }; - Ikarus::Value::Data data_type() const { - return static_cast(GetField(VT_DATA_TYPE, 0)); - } - const void *data() const { - return GetPointer(VT_DATA); - } - template const T *data_as() const; - const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { - return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { - return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { - return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::SimpleData *data_as_SimpleData() const { - return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::CombinedData *data_as_CombinedData() const { - return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ListData *data_as_ListData() const { - return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ComplexData *data_as_ComplexData() const { - return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_DATA_TYPE, 1) && - VerifyOffset(verifier, VT_DATA) && - VerifyData(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ToggleDataPoint *SimpleData::data_as() const { - return data_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *SimpleData::data_as() const { - return data_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *SimpleData::data_as() const { - return data_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *SimpleData::data_as() const { - return data_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *SimpleData::data_as() const { - return data_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *SimpleData::data_as() const { - return data_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *SimpleData::data_as() const { - return data_as_ComplexData(); -} - -struct SimpleDataBuilder { - typedef SimpleData Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data_type(Ikarus::Value::Data data_type) { - fbb_.AddElement(SimpleData::VT_DATA_TYPE, static_cast(data_type), 0); - } - void add_data(::flatbuffers::Offset data) { - fbb_.AddOffset(SimpleData::VT_DATA, data); - } - explicit SimpleDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateSimpleData( - ::flatbuffers::FlatBufferBuilder &_fbb, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset data = 0) { - SimpleDataBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_data_type(data_type); - return builder_.Finish(); -} - -struct SimpleData::Traits { - using type = SimpleData; - static auto constexpr Create = CreateSimpleData; -}; - -struct CombinedSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef CombinedSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SCHEMAS_TYPE = 4, - VT_SCHEMAS = 6 - }; - const ::flatbuffers::Vector *schemas_type() const { - return GetPointer *>(VT_SCHEMAS_TYPE); - } - const ::flatbuffers::Vector<::flatbuffers::Offset> *schemas() const { - return GetPointer> *>(VT_SCHEMAS); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_SCHEMAS_TYPE) && - verifier.VerifyVector(schemas_type()) && - VerifyOffset(verifier, VT_SCHEMAS) && - verifier.VerifyVector(schemas()) && - VerifySchemaVector(verifier, schemas(), schemas_type()) && - verifier.EndTable(); - } -}; - -struct CombinedSchemaBuilder { - typedef CombinedSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_schemas_type(::flatbuffers::Offset<::flatbuffers::Vector> schemas_type) { - fbb_.AddOffset(CombinedSchema::VT_SCHEMAS_TYPE, schemas_type); - } - void add_schemas(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas) { - fbb_.AddOffset(CombinedSchema::VT_SCHEMAS, schemas); - } - explicit CombinedSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateCombinedSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector> schemas_type = 0, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas = 0) { - CombinedSchemaBuilder builder_(_fbb); - builder_.add_schemas(schemas); - builder_.add_schemas_type(schemas_type); - return builder_.Finish(); -} - -struct CombinedSchema::Traits { - using type = CombinedSchema; - static auto constexpr Create = CreateCombinedSchema; -}; - -inline ::flatbuffers::Offset CreateCombinedSchemaDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *schemas_type = nullptr, - const std::vector<::flatbuffers::Offset> *schemas = nullptr) { - auto schemas_type__ = schemas_type ? _fbb.CreateVector(*schemas_type) : 0; - auto schemas__ = schemas ? _fbb.CreateVector<::flatbuffers::Offset>(*schemas) : 0; - return Ikarus::Value::CreateCombinedSchema( - _fbb, - schemas_type__, - schemas__); -} - -struct CombinedData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef CombinedDataBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA_TYPE = 4, - VT_DATA = 6 - }; - const ::flatbuffers::Vector *data_type() const { - return GetPointer *>(VT_DATA_TYPE); - } - const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { - return GetPointer> *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA_TYPE) && - verifier.VerifyVector(data_type()) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - VerifyDataVector(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -struct CombinedDataBuilder { - typedef CombinedData Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data_type(::flatbuffers::Offset<::flatbuffers::Vector> data_type) { - fbb_.AddOffset(CombinedData::VT_DATA_TYPE, data_type); - } - void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { - fbb_.AddOffset(CombinedData::VT_DATA, data); - } - explicit CombinedDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateCombinedData( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector> data_type = 0, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { - CombinedDataBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_data_type(data_type); - return builder_.Finish(); -} - -struct CombinedData::Traits { - using type = CombinedData; - static auto constexpr Create = CreateCombinedData; -}; - -inline ::flatbuffers::Offset CreateCombinedDataDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *data_type = nullptr, - const std::vector<::flatbuffers::Offset> *data = nullptr) { - auto data_type__ = data_type ? _fbb.CreateVector(*data_type) : 0; - auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; - return Ikarus::Value::CreateCombinedData( - _fbb, - data_type__, - data__); -} - -struct ListSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ListSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SCHEMA_TYPE = 4, - VT_SCHEMA = 6 - }; - Ikarus::Value::Schema schema_type() const { - return static_cast(GetField(VT_SCHEMA_TYPE, 0)); - } - const void *schema() const { - return GetPointer(VT_SCHEMA); - } - template const T *schema_as() const; - const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { - return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { - return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { - return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ListSchema *schema_as_ListSchema() const { - return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { - return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_SCHEMA) && - VerifySchema(verifier, schema(), schema_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ConstantSchema *ListSchema::schema_as() const { - return schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *ListSchema::schema_as() const { - return schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *ListSchema::schema_as() const { - return schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *ListSchema::schema_as() const { - return schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *ListSchema::schema_as() const { - return schema_as_ComplexSchema(); -} - -struct ListSchemaBuilder { - typedef ListSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_schema_type(Ikarus::Value::Schema schema_type) { - fbb_.AddElement(ListSchema::VT_SCHEMA_TYPE, static_cast(schema_type), 0); - } - void add_schema(::flatbuffers::Offset schema) { - fbb_.AddOffset(ListSchema::VT_SCHEMA, schema); - } - explicit ListSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateListSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset schema = 0) { - ListSchemaBuilder builder_(_fbb); - builder_.add_schema(schema); - builder_.add_schema_type(schema_type); - return builder_.Finish(); -} - -struct ListSchema::Traits { - using type = ListSchema; - static auto constexpr Create = CreateListSchema; -}; - -struct ListData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ListDataBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA_TYPE = 4, - VT_DATA = 6 - }; - const ::flatbuffers::Vector *data_type() const { - return GetPointer *>(VT_DATA_TYPE); - } - const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { - return GetPointer> *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA_TYPE) && - verifier.VerifyVector(data_type()) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - VerifyDataVector(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -struct ListDataBuilder { - typedef ListData Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data_type(::flatbuffers::Offset<::flatbuffers::Vector> data_type) { - fbb_.AddOffset(ListData::VT_DATA_TYPE, data_type); - } - void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { - fbb_.AddOffset(ListData::VT_DATA, data); - } - explicit ListDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateListData( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector> data_type = 0, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { - ListDataBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_data_type(data_type); - return builder_.Finish(); -} - -struct ListData::Traits { - using type = ListData; - static auto constexpr Create = CreateListData; -}; - -inline ::flatbuffers::Offset CreateListDataDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *data_type = nullptr, - const std::vector<::flatbuffers::Offset> *data = nullptr) { - auto data_type__ = data_type ? _fbb.CreateVector(*data_type) : 0; - auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; - return Ikarus::Value::CreateListData( - _fbb, - data_type__, - data__); -} - -struct NamedSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef NamedSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_NAME = 4, - VT_SCHEMA_TYPE = 6, - VT_SCHEMA = 8 - }; - const ::flatbuffers::String *name() const { - return GetPointer(VT_NAME); - } - Ikarus::Value::Schema schema_type() const { - return static_cast(GetField(VT_SCHEMA_TYPE, 0)); - } - const void *schema() const { - return GetPointer(VT_SCHEMA); - } - template const T *schema_as() const; - const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { - return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { - return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { - return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ListSchema *schema_as_ListSchema() const { - return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { - return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_NAME) && - verifier.VerifyString(name()) && - VerifyField(verifier, VT_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_SCHEMA) && - VerifySchema(verifier, schema(), schema_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ConstantSchema *NamedSchema::schema_as() const { - return schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *NamedSchema::schema_as() const { - return schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *NamedSchema::schema_as() const { - return schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *NamedSchema::schema_as() const { - return schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *NamedSchema::schema_as() const { - return schema_as_ComplexSchema(); -} - -struct NamedSchemaBuilder { - typedef NamedSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { - fbb_.AddOffset(NamedSchema::VT_NAME, name); - } - void add_schema_type(Ikarus::Value::Schema schema_type) { - fbb_.AddElement(NamedSchema::VT_SCHEMA_TYPE, static_cast(schema_type), 0); - } - void add_schema(::flatbuffers::Offset schema) { - fbb_.AddOffset(NamedSchema::VT_SCHEMA, schema); - } - explicit NamedSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateNamedSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::String> name = 0, - Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset schema = 0) { - NamedSchemaBuilder builder_(_fbb); - builder_.add_schema(schema); - builder_.add_name(name); - builder_.add_schema_type(schema_type); - return builder_.Finish(); -} - -struct NamedSchema::Traits { - using type = NamedSchema; - static auto constexpr Create = CreateNamedSchema; -}; - -inline ::flatbuffers::Offset CreateNamedSchemaDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const char *name = nullptr, - Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset schema = 0) { - auto name__ = name ? _fbb.CreateString(name) : 0; - return Ikarus::Value::CreateNamedSchema( - _fbb, - name__, - schema_type, - schema); -} - -struct ComplexSchema FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ComplexSchemaBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SCHEMAS = 4 - }; - const ::flatbuffers::Vector<::flatbuffers::Offset> *schemas() const { - return GetPointer> *>(VT_SCHEMAS); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_SCHEMAS) && - verifier.VerifyVector(schemas()) && - verifier.VerifyVectorOfTables(schemas()) && - verifier.EndTable(); - } -}; - -struct ComplexSchemaBuilder { - typedef ComplexSchema Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_schemas(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas) { - fbb_.AddOffset(ComplexSchema::VT_SCHEMAS, schemas); - } - explicit ComplexSchemaBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateComplexSchema( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> schemas = 0) { - ComplexSchemaBuilder builder_(_fbb); - builder_.add_schemas(schemas); - return builder_.Finish(); -} - -struct ComplexSchema::Traits { - using type = ComplexSchema; - static auto constexpr Create = CreateComplexSchema; -}; - -inline ::flatbuffers::Offset CreateComplexSchemaDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector<::flatbuffers::Offset> *schemas = nullptr) { - auto schemas__ = schemas ? _fbb.CreateVector<::flatbuffers::Offset>(*schemas) : 0; - return Ikarus::Value::CreateComplexSchema( - _fbb, - schemas__); -} - -struct NamedData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef NamedDataBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_NAME = 4, - VT_DATA_TYPE = 6, - VT_DATA = 8 - }; - const ::flatbuffers::String *name() const { - return GetPointer(VT_NAME); - } - Ikarus::Value::Data data_type() const { - return static_cast(GetField(VT_DATA_TYPE, 0)); - } - const void *data() const { - return GetPointer(VT_DATA); - } - template const T *data_as() const; - const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { - return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { - return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { - return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::SimpleData *data_as_SimpleData() const { - return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::CombinedData *data_as_CombinedData() const { - return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ListData *data_as_ListData() const { - return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ComplexData *data_as_ComplexData() const { - return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_NAME) && - verifier.VerifyString(name()) && - VerifyField(verifier, VT_DATA_TYPE, 1) && - VerifyOffset(verifier, VT_DATA) && - VerifyData(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ToggleDataPoint *NamedData::data_as() const { - return data_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *NamedData::data_as() const { - return data_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *NamedData::data_as() const { - return data_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *NamedData::data_as() const { - return data_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *NamedData::data_as() const { - return data_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *NamedData::data_as() const { - return data_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *NamedData::data_as() const { - return data_as_ComplexData(); -} - -struct NamedDataBuilder { - typedef NamedData Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { - fbb_.AddOffset(NamedData::VT_NAME, name); - } - void add_data_type(Ikarus::Value::Data data_type) { - fbb_.AddElement(NamedData::VT_DATA_TYPE, static_cast(data_type), 0); - } - void add_data(::flatbuffers::Offset data) { - fbb_.AddOffset(NamedData::VT_DATA, data); - } - explicit NamedDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateNamedData( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::String> name = 0, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset data = 0) { - NamedDataBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_name(name); - builder_.add_data_type(data_type); - return builder_.Finish(); -} - -struct NamedData::Traits { - using type = NamedData; - static auto constexpr Create = CreateNamedData; -}; - -inline ::flatbuffers::Offset CreateNamedDataDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const char *name = nullptr, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset data = 0) { - auto name__ = name ? _fbb.CreateString(name) : 0; - return Ikarus::Value::CreateNamedData( - _fbb, - name__, - data_type, - data); -} - -struct ComplexData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ComplexDataBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_DATA = 4 - }; - const ::flatbuffers::Vector<::flatbuffers::Offset> *data() const { - return GetPointer> *>(VT_DATA); - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_DATA) && - verifier.VerifyVector(data()) && - verifier.VerifyVectorOfTables(data()) && - verifier.EndTable(); - } -}; - -struct ComplexDataBuilder { - typedef ComplexData Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data) { - fbb_.AddOffset(ComplexData::VT_DATA, data); - } - explicit ComplexDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateComplexData( - ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> data = 0) { - ComplexDataBuilder builder_(_fbb); - builder_.add_data(data); - return builder_.Finish(); -} - -struct ComplexData::Traits { - using type = ComplexData; - static auto constexpr Create = CreateComplexData; -}; - -inline ::flatbuffers::Offset CreateComplexDataDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector<::flatbuffers::Offset> *data = nullptr) { - auto data__ = data ? _fbb.CreateVector<::flatbuffers::Offset>(*data) : 0; - return Ikarus::Value::CreateComplexData( - _fbb, - data__); -} - -struct Value FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef ValueBuilder Builder; - struct Traits; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SCHEMA_TYPE = 4, - VT_SCHEMA = 6, - VT_DATA_TYPE = 8, - VT_DATA = 10 - }; - Ikarus::Value::Schema schema_type() const { - return static_cast(GetField(VT_SCHEMA_TYPE, 0)); - } - const void *schema() const { - return GetPointer(VT_SCHEMA); - } - template const T *schema_as() const; - const Ikarus::Value::ConstantSchema *schema_as_ConstantSchema() const { - return schema_type() == Ikarus::Value::Schema::ConstantSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::SimpleSchema *schema_as_SimpleSchema() const { - return schema_type() == Ikarus::Value::Schema::SimpleSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::CombinedSchema *schema_as_CombinedSchema() const { - return schema_type() == Ikarus::Value::Schema::CombinedSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ListSchema *schema_as_ListSchema() const { - return schema_type() == Ikarus::Value::Schema::ListSchema ? static_cast(schema()) : nullptr; - } - const Ikarus::Value::ComplexSchema *schema_as_ComplexSchema() const { - return schema_type() == Ikarus::Value::Schema::ComplexSchema ? static_cast(schema()) : nullptr; - } - Ikarus::Value::Data data_type() const { - return static_cast(GetField(VT_DATA_TYPE, 0)); - } - const void *data() const { - return GetPointer(VT_DATA); - } - template const T *data_as() const; - const Ikarus::Value::ToggleDataPoint *data_as_ToggleDataPoint() const { - return data_type() == Ikarus::Value::Data::ToggleDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::NumberDataPoint *data_as_NumberDataPoint() const { - return data_type() == Ikarus::Value::Data::NumberDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::TextDataPoint *data_as_TextDataPoint() const { - return data_type() == Ikarus::Value::Data::TextDataPoint ? static_cast(data()) : nullptr; - } - const Ikarus::Value::SimpleData *data_as_SimpleData() const { - return data_type() == Ikarus::Value::Data::SimpleData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::CombinedData *data_as_CombinedData() const { - return data_type() == Ikarus::Value::Data::CombinedData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ListData *data_as_ListData() const { - return data_type() == Ikarus::Value::Data::ListData ? static_cast(data()) : nullptr; - } - const Ikarus::Value::ComplexData *data_as_ComplexData() const { - return data_type() == Ikarus::Value::Data::ComplexData ? static_cast(data()) : nullptr; - } - bool Verify(::flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_SCHEMA_TYPE, 1) && - VerifyOffset(verifier, VT_SCHEMA) && - VerifySchema(verifier, schema(), schema_type()) && - VerifyField(verifier, VT_DATA_TYPE, 1) && - VerifyOffset(verifier, VT_DATA) && - VerifyData(verifier, data(), data_type()) && - verifier.EndTable(); - } -}; - -template<> inline const Ikarus::Value::ConstantSchema *Value::schema_as() const { - return schema_as_ConstantSchema(); -} - -template<> inline const Ikarus::Value::SimpleSchema *Value::schema_as() const { - return schema_as_SimpleSchema(); -} - -template<> inline const Ikarus::Value::CombinedSchema *Value::schema_as() const { - return schema_as_CombinedSchema(); -} - -template<> inline const Ikarus::Value::ListSchema *Value::schema_as() const { - return schema_as_ListSchema(); -} - -template<> inline const Ikarus::Value::ComplexSchema *Value::schema_as() const { - return schema_as_ComplexSchema(); -} - -template<> inline const Ikarus::Value::ToggleDataPoint *Value::data_as() const { - return data_as_ToggleDataPoint(); -} - -template<> inline const Ikarus::Value::NumberDataPoint *Value::data_as() const { - return data_as_NumberDataPoint(); -} - -template<> inline const Ikarus::Value::TextDataPoint *Value::data_as() const { - return data_as_TextDataPoint(); -} - -template<> inline const Ikarus::Value::SimpleData *Value::data_as() const { - return data_as_SimpleData(); -} - -template<> inline const Ikarus::Value::CombinedData *Value::data_as() const { - return data_as_CombinedData(); -} - -template<> inline const Ikarus::Value::ListData *Value::data_as() const { - return data_as_ListData(); -} - -template<> inline const Ikarus::Value::ComplexData *Value::data_as() const { - return data_as_ComplexData(); -} - -struct ValueBuilder { - typedef Value Table; - ::flatbuffers::FlatBufferBuilder &fbb_; - ::flatbuffers::uoffset_t start_; - void add_schema_type(Ikarus::Value::Schema schema_type) { - fbb_.AddElement(Value::VT_SCHEMA_TYPE, static_cast(schema_type), 0); - } - void add_schema(::flatbuffers::Offset schema) { - fbb_.AddOffset(Value::VT_SCHEMA, schema); - } - void add_data_type(Ikarus::Value::Data data_type) { - fbb_.AddElement(Value::VT_DATA_TYPE, static_cast(data_type), 0); - } - void add_data(::flatbuffers::Offset data) { - fbb_.AddOffset(Value::VT_DATA, data); - } - explicit ValueBuilder(::flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ::flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = ::flatbuffers::Offset(end); - return o; - } -}; - -inline ::flatbuffers::Offset CreateValue( - ::flatbuffers::FlatBufferBuilder &_fbb, - Ikarus::Value::Schema schema_type = Ikarus::Value::Schema::NONE, - ::flatbuffers::Offset schema = 0, - Ikarus::Value::Data data_type = Ikarus::Value::Data::NONE, - ::flatbuffers::Offset data = 0) { - ValueBuilder builder_(_fbb); - builder_.add_data(data); - builder_.add_schema(schema); - builder_.add_data_type(data_type); - builder_.add_schema_type(schema_type); - return builder_.Finish(); -} - -struct Value::Traits { - using type = Value; - static auto constexpr Create = CreateValue; -}; - -inline bool VerifyData(::flatbuffers::Verifier &verifier, const void *obj, Data type) { - switch (type) { - case Data::NONE: { - return true; - } - case Data::ToggleDataPoint: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::NumberDataPoint: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::TextDataPoint: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::SimpleData: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::CombinedData: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::ListData: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Data::ComplexData: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - default: return true; - } -} - -inline bool VerifyDataVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { - if (!values || !types) return !values && !types; - if (values->size() != types->size()) return false; - for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { - if (!VerifyData( - verifier, values->Get(i), types->GetEnum(i))) { - return false; - } - } - return true; -} - -inline bool VerifySchema(::flatbuffers::Verifier &verifier, const void *obj, Schema type) { - switch (type) { - case Schema::NONE: { - return true; - } - case Schema::ConstantSchema: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Schema::SimpleSchema: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Schema::CombinedSchema: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Schema::ListSchema: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - case Schema::ComplexSchema: { - auto ptr = reinterpret_cast(obj); - return verifier.VerifyTable(ptr); - } - default: return true; - } -} - -inline bool VerifySchemaVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { - if (!values || !types) return !values && !types; - if (values->size() != types->size()) return false; - for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { - if (!VerifySchema( - verifier, values->Get(i), types->GetEnum(i))) { - return false; - } - } - return true; -} - -inline const Ikarus::Value::Value *GetValue(const void *buf) { - return ::flatbuffers::GetRoot(buf); -} - -inline const Ikarus::Value::Value *GetSizePrefixedValue(const void *buf) { - return ::flatbuffers::GetSizePrefixedRoot(buf); -} - -inline bool VerifyValueBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifyBuffer(nullptr); -} - -inline bool VerifySizePrefixedValueBuffer( - ::flatbuffers::Verifier &verifier) { - return verifier.VerifySizePrefixedBuffer(nullptr); -} - -inline void FinishValueBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.Finish(root); -} - -inline void FinishSizePrefixedValueBuffer( - ::flatbuffers::FlatBufferBuilder &fbb, - ::flatbuffers::Offset root) { - fbb.FinishSizePrefixed(root); -} - -} // namespace Value -} // namespace Ikarus - -#endif // FLATBUFFERS_GENERATED_VALUE_IKARUS_VALUE_H_ diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 9ad444e..adc97e9 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -1,7 +1,7 @@ #pragma once /// \file blueprint.h -/// \author Folling +/// \author Folling #include #include @@ -13,57 +13,113 @@ IKARUS_BEGIN_HEADER -/// \brief Blueprints are templates for managing common properties of entities. -/// \details A blueprint is a collection of properties which can be linked to entities. -/// Each entity the blueprint is linked to will have values for the blueprints -/// properties. Changes in blueprints will be reflected in all linked entities. +/// \brief Templates for sharing common properties between entities. +/// \details A blueprint allows sharing properties between entities. +/// Each entity the blueprint is linked to will have a corresponding value for +/// each of the blueprint's properties. Changes in blueprints will be reflected +/// in all linked entities. struct IkarusBlueprint; -/// \brief Creates a blueprint. -/// \param project The project the blueprint is part of. +/// \brief Flags for creating a blueprint. +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 exist. /// \param name The name of the blueprint. /// \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. +/// \param error_out \see errors.h +/// \remark Ownership remains with libikarus. IKA_API IkarusBlueprint * ikarus_blueprint_create( - struct IkarusProject * project, - char const * name, - IkarusErrorData * error_out + struct IkarusProject * project, + char const * name, + 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. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param flags Flags for deleting the blueprint. +/// \remark Must not be used after deletion. /// \param error_out \see errors.h -/// \remark The blueprint must not be accessed after deletion. IKA_API void ikarus_blueprint_delete( - IkarusBlueprint * blueprint, - IkarusErrorData * error_out + struct IkarusBlueprint * blueprint, + IkarusBlueprintDeleteFlags flags, + IkarusErrorData * error_out ); -/// \brief Gets the ID of a blueprint. -/// \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. +/// \brief Gets the project a blueprint belongs to. /// \param blueprint The blueprint to get the project of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The project or null if an error occurs. -IKA_API IkarusProject * ikarus_blueprint_get_project( - IkarusBlueprint const * blueprint, - IkarusErrorData * error_out +/// \return The project the blueprint belongs to. +/// \remark Ownership remains with libikarus. +IKA_API struct IkarusProject * ikarus_blueprint_get_project( + struct IkarusBlueprint * blueprint, + struct IkarusErrorData * error_out ); /// \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 exist. /// \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( - IkarusBlueprint const * blueprint, - IkarusErrorData * error_out + struct IkarusBlueprint * blueprint, + 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. /// \param blueprint The blueprint to set the name of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param name The new name of the blueprint. /// \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 IKA_API void ikarus_blueprint_set_name( - IkarusBlueprint * blueprint, - char const * name, - IkarusErrorData * error_out + struct IkarusBlueprint * blueprint, + char const * name, + IkarusBlueprintSetNameFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param properties_out The buffer to write the properties to. -/// \pre \li Must not be null. -/// \param properties_out_size The size of the buffer. +/// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs. /// \param error_out \see errors.h -/// \see ikarus_blueprint_get_property_count -IKA_API void ikarus_blueprint_get_properties( - IkarusBlueprint const * blueprint, - struct IkarusProperty ** properties_out, - size_t properties_out_size, - IkarusErrorData * error_out +/// \return The properties of the blueprint or null if an error occurs. +IKA_API struct IkarusProperty ** ikarus_blueprint_get_properties( + struct IkarusBlueprint * blueprint, + size_t * size_out, + struct IkarusErrorData * error_out ); /// \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 exist. /// \param error_out \see errors.h -/// \return The number of properties or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_property_count( - IkarusBlueprint const * blueprint, - IkarusErrorData * error_out +/// \return The number of properties of the blueprint or 0 if an error occurs. +IKA_API size_t ikarus_blueprint_get_properties_count( + struct IkarusBlueprint * blueprint, + struct IkarusErrorData * error_out ); -/// \brief Gets the entities linked to a blueprint. -/// \param blueprint The blueprint to get the linked entities of. +/// \brief Gets all entities linked to a blueprint. +/// \param blueprint The blueprint to get the entities of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param entities_out The buffer to write the entities to. -/// \pre \li Must not be null. -/// \param entities_out_size The size of the buffer. +/// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs. +/// \remark Ignore if null. /// \param error_out \see errors.h -/// \see ikarus_blueprint_get_linked_entity_count -IKA_API void ikarus_blueprint_get_linked_entities( - IkarusBlueprint const * blueprint, - struct IkarusEntity ** entities_out, - size_t entities_out_size, - IkarusErrorData * error_out +/// \return The entities linked to the blueprint or null if an error occurs. +IKA_API struct IkarusEntity ** ikarus_blueprint_get_entities( + struct IkarusBlueprint * blueprint, + size_t * size_out, + struct IkarusErrorData * error_out ); /// \brief Gets the number of entities linked to a blueprint. -/// \param blueprint The blueprint to get the number of linked entities of. +/// \param blueprint The blueprint to get the entities count of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The number of linked entities or undefined if an error occurs. -IKA_API size_t ikarus_blueprint_get_linked_entity_count( - IkarusBlueprint const * blueprint, - IkarusErrorData * error_out +/// \return The number of entities linked to the blueprint or 0 if an error occurs. +IKA_API size_t ikarus_blueprint_get_entities_count( + struct IkarusBlueprint * blueprint, + struct IkarusErrorData * error_out ); IKARUS_END_HEADER -// @} +/// @} diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 28cebf8..0481da7 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -5,7 +5,7 @@ #include /// \file entity.h -/// \author Folling +/// \author Folling /// \defgroup entities Entities /// \brief Entities are the core building blocks of Ikarus. @@ -13,15 +13,13 @@ IKARUS_BEGIN_HEADER /// \brief Entities are the core building blocks of Ikarus. -/// \details Entities store two data: A name and a set of values. -/// The name identifies the entity but does not have to be unique. The values -/// are the actual information of the entity. +/// \details Entities store a number of values associated with a name or property. /// -/// 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. /// 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`. /// `EntityValues` are a direct part of the entity. @@ -29,59 +27,89 @@ IKARUS_BEGIN_HEADER /// via a blueprint. struct IkarusEntity; -/// \brief Creates an entity. -/// \param project The project the entity is part of. +/// \brief Flags for creating an entity. +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 exist. /// \param name The name of the entity. /// \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 /// \return The created entity or null if an error occurs. IKA_API IkarusEntity * ikarus_entity_create( - struct IkarusProject * project, - char const * name, - IkarusErrorData * error_out + struct IkarusProject * project, + char const * name, + 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. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param flags Flags for deleting the entity. /// \param error_out \see errors.h -/// \remark The entity must not be accessed after deletion. -IKA_API void -ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out); +IKA_API void ikarus_entity_delete( + IkarusEntity * entity, + IkarusEntityDeleteFlags flags, + IkarusErrorData * error_out +); -/// \brief Gets the id of an entity. -/// \param entity The entity to get the id of. +/// \brief Flags for copying an entity. +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 exist. +/// \param flags Flags for copying the entity. /// \param error_out \see errors.h -/// \return The id of the entity or 0 if an error occurs. -IKA_API int64_t -ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out); +/// \return The copied entity or null if an error occurs. +IKA_API IkarusEntity * ikarus_entity_copy( + 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. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The project the entity is part of or null if an error occurs. -IKA_API IkarusProject * ikarus_entity_get_project( - IkarusEntity const * entity, - IkarusErrorData * error_out -); +/// \return The project the entity belongs to. +IKA_API struct IkarusProject * +ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out); /// \brief Gets the name of an entity. /// \param entity The entity 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 entity or null if an error occurs. -IKA_API char const * ikarus_entity_get_name( - IkarusEntity const * entity, - IkarusErrorData * error_out -); +/// \return The name of the entity. +IKA_API char const * +ikarus_entity_get_name(IkarusEntity * entity, 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. /// \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. /// \param name The new name of the entity. /// \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 IKA_API void ikarus_entity_set_name( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out + IkarusEntity * entity, + char const * name, + IkarusEntitySetNameFlags flags, + IkarusErrorData * error_out ); -/// \brief Checks if an entity is linked to a blueprint. -/// \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. +/// \brief Gets the blueprints an entity is linked to. /// \param entity The entity to get the blueprints of. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param blueprints_out The buffer to write the blueprints to. -/// \pre \li Must not be null. -/// \param blueprints_out_size The size of the buffer. +/// \param size_out An out parameter for the number of blueprints in the returned array +/// or undefined if an error occurs. /// \param error_out \see errors.h -/// \see ikarus_entity_get_linked_blueprint_count -IKA_API void ikarus_entity_get_linked_blueprints( - IkarusEntity const * entity, - struct IkarusBlueprint ** blueprints_out, - size_t blueprints_out_size, - IkarusErrorData * error_out +IKA_API struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( + IkarusEntity * entity, + size_t * size_out, + IkarusErrorData * error_out ); /// \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 exist. /// \param error_out \see errors.h -/// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_entity_get_linked_blueprint_count( - IkarusEntity const * entity, - IkarusErrorData * error_out +/// \return The number of linked blueprints or 0 if an error occurs. +IKA_API size_t ikarus_entity_get_linked_blueprints_count( + IkarusEntity * entity, + IkarusErrorData * error_out ); -/// \brief Checks if an entity has a value with a given name. -/// \param entity The entity to check. +/// \brief Flags for linking an entity to a blueprint. +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 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 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 -/// \return False if the entity does not have a value associated with -/// the name or if an error occurs, true otherwise. -IKA_API bool ikarus_entity_has_value( - IkarusEntity const * entity, - char const * name, - IkarusErrorData * error_out +IKA_API void ikarus_entity_link_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusEntityLinkBlueprintFlags flags, + 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. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param name The name of the value to get. -/// \pre \li Must not be null. -/// \pre \li Must be an existing value of the entity. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The value's name. +/// \pre \li Must not be null. +/// \remark Ownership remains with the client. /// \param error_out \see errors.h -/// \return The value of the entity or null if an error occurs. -IKA_API struct IkarusValue * ikarus_entity_get_value( - IkarusEntity const * entity, - char const * name, - IkarusErrorData * error_out +/// \return The value, in json format of or null if an error occurs. \see value.h +IKA_API char const * ikarus_entity_get_value( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out ); -/// \brief Sets the value of an entity. -/// If the entity does not have a value associated with the name, it is created. -/// \remark Types are overwritten if the value already exists. +/// \brief Flags for setting a value of an entity. +enum IkarusEntitySetValueFlags { + /// \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. /// \pre \li Must not be null. /// \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 exist. -/// \param value The new value of the entity. +/// \param value The value to set. /// \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 IKA_API void ikarus_entity_set_value( - IkarusEntity * entity, - char const * name, - struct IkarusValue const * value, - IkarusErrorData * error_out + IkarusEntity * entity, + char const * name, + char const * value, + IkarusEntitySetValueFlags flags, + IkarusErrorData * error_out ); -/// \brief Removes a value from an entity. -/// \pre \li The value must exist. +/// \brief Flags for deleting a value of an entity. +enum IkarusEntityDeleteValueFlags { + /// \brief No flags. + IkarusEntityDeleteValueFlags_None = 0, +}; + +/// \brief Deletes a value of an entity. /// \param entity The entity to delete the value of. /// \pre \li Must not be null. /// \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 be the name of a value which exists within the property. +/// \param flags Flags for deleting the value. /// \param error_out \see errors.h -IKA_API void ikarus_entity_remove_value( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out +IKA_API void ikarus_entity_delete_value( + IkarusEntity * entity, + char const * name, + IkarusEntityDeleteValueFlags flags, + IkarusErrorData * error_out ); -/// \brief Checks if a property is linked to an entity. -/// \param entity The entity to check. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param property The property to check. +/// \brief Gets the property values of an entity. +/// \param entity The entity to get the property values of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return False if an error occurs or the entity does not have the property, -/// true otherwise. -IKA_API bool ikarus_entity_has_property( - IkarusEntity const * entity, - struct IkarusProperty const * property, - 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 +/// \return The property values, in msgpack format of or null if an error occurs. +/// The format is a map of property pointers (as integers) to values. \see +/// value.h +IKA_API char const * ikarus_entity_get_property_values( + IkarusEntity * entity, + IkarusErrorData * error_out ); /// \brief Gets the value of a property of an entity. -/// \details If the entity has never set the value of the property, -/// the property's default value is returned. -/// \param entity The entity to get the value of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \param property The property to get the value of. -/// \pre \li Must not be null. -/// \pre \li Must exist. -/// \pre \li Must be linked to the entity. -/// \param error_out \see errors.h -/// \return The value of the property or null if an error occurs. -IKA_API struct IkarusValue * ikarus_entity_get_property_value( - IkarusEntity const * entity, - struct IkarusProperty const * property, - IkarusErrorData * error_out +/// \param entity The entity to get the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to get the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \pre \li Must be linked to the entity. +/// \param error_out \see errors.h +/// \return The value, in json format of or null if an error occurs. \see +/// value.h +IKA_API char const * ikarus_entity_get_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + 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. -/// \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 exist. /// \param property The property to set the value of. /// \pre \li Must not be null. /// \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 be of the same type as the property. -/// \pre \li Must be valid for the property's settings. +/// \pre \li Must be a valid json representation of a value. \see value.h +/// \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 -/// \remark If the entity does not have the property, this function fails. IKA_API void ikarus_entity_set_property_value( - IkarusEntity * entity, - struct IkarusProperty const * property, - struct IkarusValue const * value, - IkarusErrorData * error_out + IkarusEntity * entity, + struct IkarusProperty * property, + char const * value, + IkarusEntitySetPropertyValueFlags flags, + IkarusErrorData * error_out ); IKARUS_END_HEADER -// @} +/// @} diff --git a/include/ikarus/objects/properties/number_property.h b/include/ikarus/objects/properties/number_property.h deleted file mode 100644 index d989ec2..0000000 --- a/include/ikarus/objects/properties/number_property.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -/// \file number_property.h -/// \author Folling - -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/objects/properties/property.h b/include/ikarus/objects/properties/property.h deleted file mode 100644 index c7ba19b..0000000 --- a/include/ikarus/objects/properties/property.h +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -/// \file property.h -/// \author Folling - -#include -#include -#include -#include -#include - -/// \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 - -// @} diff --git a/include/ikarus/objects/properties/text_property.h b/include/ikarus/objects/properties/text_property.h deleted file mode 100644 index a0c0a9d..0000000 --- a/include/ikarus/objects/properties/text_property.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -/// \file text_property.h -/// \author Folling - -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/objects/properties/toggle_property.h b/include/ikarus/objects/properties/toggle_property.h deleted file mode 100644 index bfec311..0000000 --- a/include/ikarus/objects/properties/toggle_property.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -/// \file toggle_property.h -/// \author Folling - -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h new file mode 100644 index 0000000..5bff42c --- /dev/null +++ b/include/ikarus/objects/property.h @@ -0,0 +1,143 @@ +#pragma once + +/// \file property.h +/// \author Folling + +#include +#include +#include + +/// \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 + +/// @} diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 74258a9..af40147 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -1,7 +1,9 @@ #pragma once /// \file project.h -/// \author Folling +/// \author Folling + +#include #include #include @@ -15,10 +17,19 @@ IKARUS_BEGIN_HEADER /// \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. 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. /// \param path The path to the project. /// \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. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param flags Flags for creating the project. /// \param error_out \see errors.h /// \return The created project or null if an error occurs. -/// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use -/// ikarus_project_delete -IKA_API IkarusProject * ikarus_project_create( - char const * path, - char const * name, - IkarusErrorData * error_out +/// \remark Must be closed with #ikarus_project_close. +IKA_API struct IkarusProject * ikarus_project_create( + char const * path, + char const * name, + IkarusProjectCreateFlags flags, + 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. /// \pre \li Must not be null. /// \pre \li Must not be empty. +/// \param flags Flags for creating the project. /// \param error_out \see errors.h /// \return The created project or null if an error occurs. -/// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use -/// ikarus_project_delete -IKA_API IkarusProject * -ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out); +/// \remark Must be closed with #ikarus_project_close. +IKA_API struct IkarusProject * ikarus_project_create_in_memory( + char const * name, + IkarusProjectCreateInMemoryFlags flags, + IkarusErrorData * error_out +); + +/// \brief Flags for opening a project. +enum IkarusProjectOpenFlags { + /// \brief No flags. + IkarusProjectOpenFlags_None = 0, +}; /// \brief Opens an existing project. /// \param path The path to the project. /// \pre \li Must not be null. /// \pre \li Must point to an existing project on the system. +/// \param flags Flags for opening the project. /// \param error_out \see errors.h /// \return The opened project or null if an error occurs. -/// \remark Must be freed using ikarus_free. Freeing does not delete the project from the filesystem. For that, use -/// ikarus_project_delete -IKA_API IkarusProject * -ikarus_project_open(char const * path, 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 +/// \remark Must be closed with #ikarus_project_close. +IKA_API struct IkarusProject * ikarus_project_open( + char const * path, + IkarusProjectOpenFlags flags, + IkarusErrorData * error_out ); -/// \brief Sets the name of a project. -/// \param project The project to set the name of. +/// \brief Flags for closing a project in memory. +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 exist. -/// \param new_name The new name of the project. -/// \pre \li Must not be null. -/// \pre \li Must not be empty. +/// \param flags Flags for deleting the project. /// \param error_out \see errors.h -IKA_API void ikarus_project_set_name( - IkarusProject * project, - char const * new_name, - IkarusErrorData * error_out +/// \remark The project must not be used after deletion. +/// \remark Mutually exclusive with #ikarus_project_close. +IKA_API void ikarus_project_delete( + 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. -/// \param project The project to get the path of. +/// \param project The project to delete. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The path of the project. -/// \remark Ownership remains with libikarus, must not be freed. +/// \returns The path of the project or null if an error occurs. IKA_API char const * ikarus_project_get_path( - IkarusProject const * project, - IkarusErrorData * error_out + struct IkarusProject const * project, + IkarusErrorData * error_out ); -/// \brief Gets the entities of a project. -/// \param project The project to get the entities of. +/// \brief Gets all entities in a project. +/// \param project The project from which to get the entities. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param entities_out The buffer to write the entities to. -/// \pre \li Must not be null. -/// \param entities_out_size The size of the buffer. +/// \param size_out An out parameter for the number of entities in the returned array +/// or undefined if an error occurs. /// \param error_out \see errors.h -IKA_API void ikarus_project_get_entities( - IkarusProject * project, - struct IkarusEntity ** entities_out, - size_t entities_out_size, - IkarusErrorData * error_out +/// \returns An array of entities or null if an error occurs. +IKA_API struct IkarusEntity ** ikarus_project_get_entities( + struct IkarusProject const * project, + size_t * size_out, + IkarusErrorData * error_out ); -/// \brief Gets the number of entities of a project. -/// \param project The project to get the number of entities of. +/// \brief Gets how many entities in a project there are. +/// \param project The project from which to get the count. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The number of entities or undefined if an error occurs. -IKA_API size_t ikarus_project_get_entity_count( - IkarusProject const * project, - IkarusErrorData * error_out +/// \return The count or 0 if an error occurs. +IKA_API size_t ikarus_project_get_entities_count( + IkarusProject const * project, + IkarusErrorData * error_out ); -/// \brief Gets the blueprints of a project. -/// \param project The project to get the blueprints of. +/// \brief Gets all blueprints from a project. +/// \param project The project from which to get the blueprints. /// \pre \li Must not be null. /// \pre \li Must exist. -/// \param blueprints_out The buffer to write the blueprints to. -/// \pre \li Must not be null. -/// \param blueprints_out_size The size of the buffer. +/// \param size_out An out parameter for the number of blueprints in the returned array +/// or undefined if an error occurs. /// \param error_out \see errors.h -IKA_API void ikarus_project_get_blueprints( - IkarusProject * project, - struct IkarusBlueprint ** blueprints_out, - size_t blueprints_out_size, - IkarusErrorData * error_out +/// \return An array of blueprints or null if an error occurs. +IKA_API struct IkarusBlueprint ** ikarus_project_get_blueprints( + struct IkarusProject const * project, + size_t * size_out, + IkarusErrorData * error_out ); -/// \brief Gets the number of blueprints of a project. -/// \param project The project to get the number of blueprints of. +/// \brief Gets how many blueprints in a project there are. +/// \param project The project from which to get the count. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param error_out \see errors.h -/// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count( - IkarusProject const * project, - IkarusErrorData * error_out +/// \return The count or 0 if an error occurs. +IKA_API size_t ikarus_project_get_blueprints_count( + IkarusProject const * project, + IkarusErrorData * error_out ); IKARUS_END_HEADER diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h index 4274e3f..71d07c2 100644 --- a/include/ikarus/stdtypes.h +++ b/include/ikarus/stdtypes.h @@ -1,10 +1,11 @@ #pragma once #ifdef __cplusplus -#include -#include + #include + #include using std::size_t; #else -#include -#include + #include + #include + #include #endif diff --git a/include/ikarus/values/data.h b/include/ikarus/values/data.h new file mode 100644 index 0000000..d4e2367 --- /dev/null +++ b/include/ikarus/values/data.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include + +/// \file data.h +/// \author Folling + +/// \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 diff --git a/include/ikarus/values/number_value.h b/include/ikarus/values/number_value.h deleted file mode 100644 index 220a11c..0000000 --- a/include/ikarus/values/number_value.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -/// \file number_value.h -/// \author Folling - -#include -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/values/schema.h b/include/ikarus/values/schema.h new file mode 100644 index 0000000..9e25e01 --- /dev/null +++ b/include/ikarus/values/schema.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include + +/// \file schema.h +/// \author Folling + +/// \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 diff --git a/include/ikarus/values/text_value.h b/include/ikarus/values/text_value.h deleted file mode 100644 index 3b7d978..0000000 --- a/include/ikarus/values/text_value.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -/// \file text_value.h -/// \author Folling - -#include -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/values/toggle_value.h b/include/ikarus/values/toggle_value.h deleted file mode 100644 index 05c5ddc..0000000 --- a/include/ikarus/values/toggle_value.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -/// \file toggle_value.h -/// \author Folling - -#include -#include -#include - -/// \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 - -/// @} diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index aec86ac..870f551 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -1,61 +1,26 @@ #pragma once -/// \file value.h -/// \author Folling - -/// \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. 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 #include +#include + +/// \file value.h +/// \author Folling + +/// \defgroup entities Entities +/// \brief Entities are the core building blocks of Ikarus. IKARUS_BEGIN_HEADER -/// \brief The common type for all value data. -struct IkarusValue; - -/// \brief Visits an entity value, -/// calling the appropriate function for the value's type. -/// \param value The entity value to visit. -/// \pre \li Must not be null. -/// \param toggle_visitor The function to call if the value is a toggle value. -/// \remark Skipped if null. -/// \param number_visitor The function to call if the value is a number value. -/// \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 -); +/// \brief Values are data containers for entities. +/// \details Values are flexible enough to store any kind of data. They are +/// akin to objects in programming languages. +/// Each value has a schema that defines the type of the value. \see schema.h +/// They also store data appropriate for the schema. +/// +/// Given the complexity of values, they are transferred as json. +/// The json representation of a value is a map with the following keys: +/// - `schema`: The schema of the value. \see schema.h. +/// - `data`: The data of the value. \see data.h. IKARUS_END_HEADER - -/// @} diff --git a/include/ikarus/values/value_cardinality.h b/include/ikarus/values/value_cardinality.h deleted file mode 100644 index 6d5526f..0000000 --- a/include/ikarus/values/value_cardinality.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -/// \file value_cardinality.h -/// \author Folling - -/// \addtogroup values Values -/// @{ - -#include - -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 - -/// @} diff --git a/include/ikarus/values/value_type.h b/include/ikarus/values/value_type.h deleted file mode 100644 index caa6165..0000000 --- a/include/ikarus/values/value_type.h +++ /dev/null @@ -1,198 +0,0 @@ -#pragma once - -/// \file value_schema.h -/// \author Folling - -/// \addtogroup values Values -/// @{ - -#include -#include -#include - -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, Number, Complex<"Foo":Toggle,"Bar":Text> -struct IkarusValueLayout; -/// \brief A fixed datapoint with a fixed layout. -/// Example: Age: Union> -struct IkarusValueLayoutConstant; -/// \brief A singular datapoint with one of a list of layouts. -/// Example: ChestReward: Combination -struct IkarusValueLayoutCombination; -/// \brief A collection of datapoints with a homogenous layout. -/// Example: Friends: List -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 - -/// @} diff --git a/include/module.modulemap b/include/module.modulemap deleted file mode 100644 index 6b2dbcb..0000000 --- a/include/module.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -module ikarus { - header "ikarus/persistence/project.h" - header "ikarus/objects/entity.h" - header "ikarus/objects/blueprint.h" - export * -} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0963d2e..b928e93 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,4 +7,4 @@ file( "*.c" ) -set(SOURCE_FILES ${FILES} PARENT_SCOPE) \ No newline at end of file +set(SOURCE_FILES ${FILES} PARENT_SCOPE) diff --git a/src/ikarus/errors.cpp b/src/ikarus/errors.cpp index 8f8ff3c..b3e4ae2 100644 --- a/src/ikarus/errors.cpp +++ b/src/ikarus/errors.cpp @@ -1,41 +1,72 @@ #include "ikarus/errors.h" +#include + +#include + +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) { - switch (info) { - case IkarusErrorInfo_None: return "None"; + switch (info) { + case IkarusErrorInfo_None: return "None"; - case IkarusErrorInfo_Client_Misuse: return "Client::Misuse"; - case IkarusErrorInfo_Client_InvalidInput: return "Client::InvalidInput"; - case IkarusErrorInfo_Client_InvalidFormat: return "Client::InvalidFormat"; - case IkarusErrorInfo_Client_ConstraintViolated: return "Client::ConstraintViolated"; + case IkarusErrorInfo_Client_Misuse: return "Client::Misuse"; + case IkarusErrorInfo_Client_InvalidInput: return "Client::InvalidInput"; + case IkarusErrorInfo_Client_NonExistent: return "Client::NonExistent"; + case IkarusErrorInfo_Client_InvalidFormat: return "Client::InvalidFormat"; + case IkarusErrorInfo_Client_ConstraintViolated: + return "Client::ConstraintViolated"; - case IkarusErrorInfo_Filesystem_NotFound: return "Filesystem::NotFound"; - case IkarusErrorInfo_Filesystem_AlreadyExists: return "Filesystem::AlreadyExists"; - case IkarusErrorInfo_Filesystem_MissingPermissions: return "Filesystem::MissingPermissions"; - case IkarusErrorInfo_Filesystem_InsufficientSpace: return "Filesystem::InsufficientSpace"; - case IkarusErrorInfo_Filesystem_InvalidPath: return "Filesystem::InvalidPath"; + case IkarusErrorInfo_Filesystem_NotFound: return "Filesystem::NotFound"; + case IkarusErrorInfo_Filesystem_AlreadyExists: + return "Filesystem::AlreadyExists"; + case IkarusErrorInfo_Filesystem_MissingPermissions: + 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_QueryFailed: return "Database::QueryFailed"; - case IkarusErrorInfo_Database_MigrationFailed: return "Database::MigrationFailed"; - case IkarusErrorInfo_Database_InvalidState: return "Database::InvalidState"; + case IkarusErrorInfo_Database_ConnectionFailed: + return "Database::ConnectionFailed"; + case IkarusErrorInfo_Database_QueryFailed: return "Database::QueryFailed"; + case IkarusErrorInfo_Database_MigrationFailed: + return "Database::MigrationFailed"; + case IkarusErrorInfo_Database_InvalidState: return "Database::InvalidState"; - case IkarusErrorInfo_OS_SystemCallFailed: return "OS::SystemCallFailed"; - case IkarusErrorInfo_OS_InvalidReturnValue: return "OS::InvalidReturnValue"; - case IkarusErrorInfo_OS_InsufficientMemory: return "OS::InsufficientMemory"; + case IkarusErrorInfo_OS_SystemCallFailed: return "OS::SystemCallFailed"; + case IkarusErrorInfo_OS_InvalidReturnValue: return "OS::InvalidReturnValue"; + case IkarusErrorInfo_OS_InsufficientMemory: return "OS::InsufficientMemory"; - case IkarusErrorInfo_LibIkarus_InvalidState: return "LibIkarus::InvalidState"; - case IkarusErrorInfo_LibIkarus_CannotPerformOperation: return "LibIkarus::CannotPerformOperation"; - case IkarusErrorInfo_LibIkarus_Timeout: return "LibIkarus::Timeout"; + case IkarusErrorInfo_LibIkarus_InvalidState: + return "LibIkarus::InvalidState"; + 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) { - return data->info == IkarusErrorInfo_None; + return data->info == IkarusErrorInfo_None; } bool ikarus_error_data_is_error(IkarusErrorData const * data) { - return data->info != IkarusErrorInfo_None; + return data->info != IkarusErrorInfo_None; } diff --git a/src/ikarus/errors.hpp b/src/ikarus/errors.hpp index e12a68f..55b3e8f 100644 --- a/src/ikarus/errors.hpp +++ b/src/ikarus/errors.hpp @@ -1,80 +1,169 @@ #pragma once #include +#include #include -inline void safe_strcpy(char * dest, std::string_view src, size_t dest_size) { - for (int i = 0; i < dest_size; ++i) { - if (src[i] == '\0') { - dest[i] = '\0'; - return; - } +void safe_strcpy( + std::string_view const src, + char * dest, + size_t const dest_size +); - dest[i] = src[i]; - } -} - -#define IKARUS_SET_ERROR(msg, err_info) \ - if (error_out != nullptr) { \ - safe_strcpy(static_cast(error_out->message), msg, IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT); \ - error_out->info = err_info; \ - } +#define IKARUS_SET_ERROR(msg, err_info) \ + if (error_out != nullptr) { \ + safe_strcpy( \ + msg, \ + static_cast(error_out->message), \ + IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT \ + ); \ + error_out->info = err_info; \ + } #define IKARUS_FAIL(ret, msg, err_info) \ - IKARUS_SET_ERROR(msg, err_info); \ - return ret + IKARUS_SET_ERROR(msg, err_info); \ + return ret #define IKARUS_FAIL_IF(condition, ret, msg, err_info) \ - if (condition) { \ - IKARUS_SET_ERROR(msg, err_info) \ - return ret; \ - } + if (condition) { \ + IKARUS_SET_ERROR(msg, err_info) \ + return ret; \ + } -#define IKARUS_FAIL_IF_ERROR(ret) \ - if (ikarus_error_data_is_error(error_out)) { \ - return ret; \ - } +#define IKARUS_TRY_OR_FAIL_IMPL(var_name, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + 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, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ - return var_name; \ - } - -#define IKARUS_TRY_OR_FAIL(msg, err_info, ...) IKARUS_TRY_OR_FAIL_IMPL(CPPBASE_UNIQUE_NAME(result), msg, err_info, __VA_ARGS__); - -#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ - return ret; \ - } +#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR( \ + fmt::format( \ + fmt::runtime(msg), \ + std::move(var_name).unwrap_error() \ + ), \ + err_info \ + ); \ + return ret; \ + } #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, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ - return var_name; \ - } \ - value = var_name.unwrap_value() +#define IKARUS_VTRY_OR_FAIL_IMPL(var_name, value, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR( \ + fmt::format( \ + fmt::runtime(msg), \ + 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, ...) \ - 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, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR(fmt::format(msg, var_name.unwrap_error()), err_info); \ - return ret; \ - } \ - value = var_name.unwrap_value() +#define IKARUS_VTRYRV_OR_FAIL_IMPL(var_name, value, ret, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR( \ + fmt::format(fmt::runtime(msg), var_name.unwrap_error()), \ + err_info \ + ); \ + return ret; \ + } \ + value = std::move(var_name).unwrap_value() #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::object_name \ + ), \ + IkarusErrorInfo_Database_QueryFailed, \ + object->project->db->query_one( \ + fmt::format( \ + "SELECT EXISTS(SELECT 1 FROM `{}` WHERE `id` = ?)", \ + std::remove_cvref_t::table_name \ + ), \ + object->id \ + ) \ + ); \ + \ + IKARUS_FAIL_IF( \ + !exists, \ + ret, \ + fmt::format( \ + "{} doesn't exist", \ + std::remove_cvref_t::object_name \ + ), \ + IkarusErrorInfo_Client_NonExistent \ + ) diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index a2a5673..c25961f 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -1,168 +1,5 @@ #include "ikarus/objects/blueprint.h" -#include -#include -#include - #include #include -#include -#include -#include -#include #include -#include - -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 { - 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 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( - "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("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( - "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("SELECT COUNT(`entity`) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id) - ); - - return ret; -} diff --git a/src/ikarus/objects/blueprint.hpp b/src/ikarus/objects/blueprint.hpp index cd46d62..b56143f 100644 --- a/src/ikarus/objects/blueprint.hpp +++ b/src/ikarus/objects/blueprint.hpp @@ -1,19 +1,23 @@ #pragma once -#include +#include -struct IkarusBlueprint : public IkarusObject { -public: - IkarusBlueprint(struct IkarusProject * project, int64_t id); +struct IkarusBlueprint { + consteval static inline auto OBJECT_NAME() -> std::string_view { + return "blueprint"; + } - IkarusBlueprint(IkarusBlueprint const &) = default; - IkarusBlueprint(IkarusBlueprint &&) = default; + consteval static inline auto TABLE_NAME() -> std::string_view { + return "blueprints"; + } - IkarusBlueprint & operator=(IkarusBlueprint const &) = default; - IkarusBlueprint & operator=(IkarusBlueprint &&) = default; + IkarusBlueprint( + struct IkarusProject * project, + int64_t id, + std::string_view name + ); - ~IkarusBlueprint() override = default; - -public: - std::string_view get_table_name() const noexcept override; + struct IkarusProject * project; + int64_t id; + std::string name; }; diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 2df1404..47f1f90 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -1,488 +1,171 @@ -#include "ikarus/objects/entity.h" - -#include +#include "entity.hpp" +#include #include -#include -#include -#include -#include +#include #include -#include -#include -#include -IkarusEntity::IkarusEntity(IkarusProject * project, int64_t id): - IkarusObject{project, id} {} - -std::string_view IkarusEntity::get_table_name() const noexcept { - return "entities"; -} +IkarusEntity::IkarusEntity( + struct IkarusProject * project, + int64_t id, + std::string_view name +): + project{project}, + id{id}, + name{name} {} IkarusEntity * ikarus_entity_create( - struct IkarusProject * project, - char const * name, - IkarusErrorData * error_out + struct IkarusProject * project, + char const * name, + IkarusEntityCreateFlags flags, + IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); - IKARUS_VTRYRV_OR_FAIL( - int64_t const id, - nullptr, - "failed to create entity: {}", - IkarusErrorInfo_Database_QueryFailed, - project->db->transact( - [name](auto * db - ) -> cppbase::Result { - TRY(db->execute( - "INSERT INTO `entities`(`name`) VALUES(?, ?)", - name - )); - return cppbase::ok(db->last_insert_rowid()); - } - ) - ); + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to create entity: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->execute("INSERT INTO `entities`(`name`) VALUES(?)", name) + ); - 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) { - IKARUS_FAIL_IF_NULL(entity, ); - IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); - - 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 +void ikarus_entity_delete( + IkarusEntity * entity, + IkarusEntityDeleteFlags flags, + 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 const * entity, - IkarusErrorData * error_out +IkarusEntity * ikarus_entity_copy( + IkarusEntity * entity, + 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 { + 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( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out + IkarusEntity * entity, + char const * name, + 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( - IkarusEntity const * entity, - struct IkarusBlueprint const * blueprint, - IkarusErrorData * error_out +char const * ikarus_entity_get_value( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, false); - IKARUS_FAIL_IF_OBJECT_MISSING(entity, false); - 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( - "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( - "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( - "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( - "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; + IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); } void ikarus_entity_set_value( - IkarusEntity * entity, - char const * name, - struct IkarusValue const * value, - IkarusErrorData * error_out + IkarusEntity * entity, + char const * name, + char const * value, + IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, ); - IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); - IKARUS_FAIL_IF_NAME_INVALID(name, ); - 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( - "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 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( - "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( - "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()) - ) - ); + IKARUS_FAIL_IF_NULL(entity, ); + IKARUS_FAIL_IF_NULL(name, ); + IKARUS_FAIL_IF_NULL(value, ); } diff --git a/src/ikarus/objects/entity.hpp b/src/ikarus/objects/entity.hpp index e3bc035..90a445e 100644 --- a/src/ikarus/objects/entity.hpp +++ b/src/ikarus/objects/entity.hpp @@ -1,19 +1,24 @@ #pragma once -#include +#include +#include -struct IkarusEntity : public IkarusObject { -public: - inline IkarusEntity(struct IkarusProject * project, int64_t id); +#include - IkarusEntity(IkarusEntity const &) = default; - IkarusEntity(IkarusEntity &&) = default; +struct IkarusEntity { + constinit static inline auto object_name = "entity"; + constinit static inline auto table_name = "entities"; - IkarusEntity & operator=(IkarusEntity const &) = default; - IkarusEntity & operator=(IkarusEntity &&) = default; + IkarusEntity( + struct IkarusProject * project, + int64_t id, + std::string_view name + ); - ~IkarusEntity() override = default; + struct IkarusProject * project; + int64_t id; + std::string name; -public: - std::string_view get_table_name() const noexcept override; + std::vector> values_ordered; + std::unordered_map values; }; diff --git a/src/ikarus/objects/object.cpp b/src/ikarus/objects/object.cpp deleted file mode 100644 index 01747cc..0000000 --- a/src/ikarus/objects/object.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "object.hpp" - -#include - -IkarusObject::IkarusObject(IkarusProject * project, int64_t id): - project{project}, - id{id} {} diff --git a/src/ikarus/objects/object.hpp b/src/ikarus/objects/object.hpp deleted file mode 100644 index 96d5911..0000000 --- a/src/ikarus/objects/object.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -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("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); diff --git a/src/ikarus/objects/properties/number_property.cpp b/src/ikarus/objects/properties/number_property.cpp deleted file mode 100644 index 60c9ccd..0000000 --- a/src/ikarus/objects/properties/number_property.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "number_property.hpp" - -#include - -#include -#include -#include -#include - -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(project, name, property_source, error_out); -} - -IkarusNumberValue * ikarus_number_property_get_default_value(IkarusNumberProperty * property, IkarusErrorData * error_out) { - return ikarus::util::get_default_value(property, error_out); -} - -void ikarus_number_property_set_default_value( - IkarusNumberProperty * property, - IkarusNumberValue * new_default_value, - IkarusErrorData * error_out -) { - ikarus::util::set_default_value(property, new_default_value, error_out); -} diff --git a/src/ikarus/objects/properties/number_property.hpp b/src/ikarus/objects/properties/number_property.hpp deleted file mode 100644 index 55ff25c..0000000 --- a/src/ikarus/objects/properties/number_property.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include -#include - -struct IkarusNumberProperty : public IkarusProperty { -public: - using value_type = IkarusNumberValue; - constexpr auto static PropertyType = IkarusValueType_Number; - -public: - IkarusNumberProperty(struct IkarusProject * project, int64_t id); -}; diff --git a/src/ikarus/objects/properties/property.cpp b/src/ikarus/objects/properties/property.cpp deleted file mode 100644 index ecc2f3e..0000000 --- a/src/ikarus/objects/properties/property.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "property.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include - -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("SELECT `type` FROM `properties` WHERE `id` = ?", property->id) - ); - - return static_cast(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("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(property); -} - -IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property) { - return static_cast(property); -} diff --git a/src/ikarus/objects/properties/property.hpp b/src/ikarus/objects/properties/property.hpp deleted file mode 100644 index 1fd0394..0000000 --- a/src/ikarus/objects/properties/property.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include - -struct IkarusProperty : public IkarusObject { -public: - using Data = std::variant; - -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; -}; diff --git a/src/ikarus/objects/properties/text_property.cpp b/src/ikarus/objects/properties/text_property.cpp deleted file mode 100644 index c51d653..0000000 --- a/src/ikarus/objects/properties/text_property.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "text_property.hpp" - -#include - -#include -#include -#include -#include - -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(project, name, property_source, error_out); -} - -IkarusTextValue * ikarus_text_property_get_default_value(IkarusTextProperty * property, IkarusErrorData * error_out) { - return ikarus::util::get_default_value(property, error_out); -} - -void ikarus_text_property_set_default_value( - IkarusTextProperty * property, - IkarusTextValue * new_default_value, - IkarusErrorData * error_out -) { - ikarus::util::set_default_value(property, new_default_value, error_out); -} diff --git a/src/ikarus/objects/properties/text_property.hpp b/src/ikarus/objects/properties/text_property.hpp deleted file mode 100644 index b690cda..0000000 --- a/src/ikarus/objects/properties/text_property.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include -#include - -struct IkarusTextProperty : public IkarusProperty { -public: - using value_type = IkarusTextValue; - constexpr auto static PropertyType = IkarusValueType_Text; - -public: - IkarusTextProperty(struct IkarusProject * project, int64_t id); -}; diff --git a/src/ikarus/objects/properties/toggle_property.cpp b/src/ikarus/objects/properties/toggle_property.cpp deleted file mode 100644 index d1705a2..0000000 --- a/src/ikarus/objects/properties/toggle_property.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "toggle_property.hpp" - -#include - -#include -#include -#include -#include - -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(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(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(property, new_default_value, error_out); -} diff --git a/src/ikarus/objects/properties/toggle_property.hpp b/src/ikarus/objects/properties/toggle_property.hpp deleted file mode 100644 index 35575ee..0000000 --- a/src/ikarus/objects/properties/toggle_property.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include -#include - -struct IkarusToggleProperty { -public: - using value_type = IkarusToggleValue; - constexpr auto static PropertyType = IkarusValueType_Toggle; - -public: - IkarusToggleProperty(struct IkarusProject * project, int64_t id); - -public: - IkarusProperty * property; -}; diff --git a/src/ikarus/objects/properties/util.hpp b/src/ikarus/objects/properties/util.hpp deleted file mode 100644 index 97146f1..0000000 --- a/src/ikarus/objects/properties/util.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace ikarus::util { -template -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 { - 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(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().pretty_name()), - IkarusErrorInfo_LibIkarus_InvalidState - ); - - return ret; -} - -template -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(&value->data); - - IKARUS_FAIL_IF( - ret == nullptr, - nullptr, - fmt::format( - "{} default value is not a(n) {}", - boost::typeindex::type_id().pretty_name(), - boost::typeindex::type_id().pretty_name() - ), - IkarusErrorInfo_Database_InvalidState - ); - - return *ret; -} - -template -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 diff --git a/src/ikarus/objects/property.cpp b/src/ikarus/objects/property.cpp new file mode 100644 index 0000000..d096b18 --- /dev/null +++ b/src/ikarus/objects/property.cpp @@ -0,0 +1,15 @@ +#include "property.hpp" + +#include +#include +#include +#include + +IkarusProperty::IkarusProperty( + struct IkarusProject * project, + int64_t id, + std::string_view name +): + project{project}, + id{id}, + name{name} {} diff --git a/src/ikarus/objects/property.hpp b/src/ikarus/objects/property.hpp new file mode 100644 index 0000000..29143fb --- /dev/null +++ b/src/ikarus/objects/property.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +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; +}; diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp deleted file mode 100644 index e625f5d..0000000 --- a/src/ikarus/objects/util.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include -#include - -namespace ikarus::util { - -template -[[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 -[[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 -[[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(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 -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 diff --git a/src/ikarus/persistence/migrations.hpp b/src/ikarus/persistence/migrations.hpp index db649a5..a3a1d5e 100644 --- a/src/ikarus/persistence/migrations.hpp +++ b/src/ikarus/persistence/migrations.hpp @@ -1,7 +1,5 @@ #pragma once -#include - #include #include @@ -9,7 +7,10 @@ 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 { public: @@ -27,7 +28,11 @@ public: std::string_view sql; }; -#define DECLARE_MIGRATION(name) std::make_unique(static_cast(name()), name##_size()) +#define DECLARE_MIGRATION(name) \ + std::make_unique( \ + static_cast(name()), \ + name##_size() \ + ) constexpr std::string_view DB_VERSION_KEY = "IKARUS_DB_VERSION"; std::vector> const MIGRATIONS = []() { diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index aa28c8a..8b263d7 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -6,13 +6,14 @@ CREATE TABLE `entities` CREATE TABLE `entity_values` ( + `id` INTEGER PRIMARY KEY, `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `name` TEXT NOT NULL, - `type` INT NOT NULL, `value` TEXT NOT NULL, - PRIMARY KEY (`entity`, `name`) -) WITHOUT ROWID, STRICT; + PRIMARY KEY (`id`), + UNIQUE (`entity`, `name`) +) STRICT; CREATE TABLE `blueprints` ( @@ -25,14 +26,11 @@ CREATE TABLE `properties` `id` INTEGER PRIMARY KEY, `blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE, `name` TEXT NOT NULL, - `type` INT NOT NULL, - `cardinality` INT NOT NULL, + `schema` TEXT NOT NULL, `default_value` TEXT NOT NULL, `settings` TEXT NOT NULL ) STRICT; -CREATE INDEX `properties_type` ON `properties` (`type`); - CREATE TABLE `entity_blueprint_links` ( `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, @@ -43,9 +41,11 @@ CREATE TABLE `entity_blueprint_links` CREATE TABLE `entity_property_values` ( + `id` INTEGER PRIMARY KEY, `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `property` INTEGER NOT NULL REFERENCES `properties` (`id`) ON DELETE CASCADE, `value` TEXT NOT NULL, - PRIMARY KEY (`entity`, `property`) -) WITHOUT ROWID, STRICT; + PRIMARY KEY (`id`), + UNIQUE (`entity`, `property`) +) STRICT; diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 0d5b1e5..300bf86 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -1,339 +1,348 @@ #include "ikarus/persistence/project.h" +#include + #include +#include #include +#include +#include +#include #include #include -#include -#include -#include -#include -#include #include #include -IkarusProject::IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && db): - name{name}, - path{std::move(path)}, - db{std::move(db)}, - _blueprints{}, - _properties{}, - _entities{} {} +constexpr char const * DB_PROJECT_ID_KEY = "project_id"; +constexpr char const * DB_PROJECT_NAME_KEY = "project_name"; -IkarusBlueprint * IkarusProject::get_blueprint(int64_t id) { - return get_cached_object(id, this->_blueprints); +auto create_impl( + std::string_view path, + std::string_view name, + IkarusErrorData * error_out +) -> std::unique_ptr { + 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 { - remove_cached_object(blueprint, _blueprints); -} - -auto IkarusProject::get_entity(int64_t id) -> IkarusEntity * { - return get_cached_object(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(this, id)).first->second.get(); - case IkarusValueType_Number: - return _properties.emplace(id, std::make_unique(this, id)).first->second.get(); - case IkarusValueType_Text: return _properties.emplace(id, std::make_unique(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("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 +IkarusProject * ikarus_project_create( + char const * path, + char const * name, + IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, ); - IKARUS_FAIL_IF_NULL(blueprints_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 + ); - if (blueprints_out_size == 0) { - return; - } + boost::filesystem::path fs_path{path}; - 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( - , - "unable to fetch project blueprints from database: {}", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered("SELECT `id` FROM `blueprints`", ids, blueprints_out_size) - ); + if (auto db = create_impl(fs_path.c_str(), name, error_out); !db) { + LOG_WARN("creating project failed, removing file at {}", path); - for (size_t i = 0; i < blueprints_out_size; ++i) { - blueprints_out[i] = project->get_blueprint(ids[i]); - } + boost::system::error_code ec; + 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) { - IKARUS_FAIL_IF_NULL(project, 0); +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 const ret, - 0, - "unable to fetch project blueprint count from database: {}", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_one("SELECT COUNT(*) FROM `blueprints`") - ); + if (auto db = create_impl(":memory:", name, error_out); !db) { + return nullptr; + } else { + IKARUS_TRYRV_OR_FAIL( + nullptr, + "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( + "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( - IkarusProject * project, - struct IkarusEntity ** entities_out, - size_t entities_out_size, - IkarusErrorData * error_out + struct IkarusProject const * project, + int64_t * ids_out, + uint64_t ids_out_size, + IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, ); - IKARUS_FAIL_IF_NULL(entities_out, ); + IKARUS_FAIL_IF_NULL(project, ); + IKARUS_FAIL_IF_NULL(ids_out, ); - if (entities_out_size == 0) { - return; - } + if (ids_out == 0) { + return; + } - int64_t ids[entities_out_size]; - - IKARUS_TRYRV_OR_FAIL( - , - "unable to fetch project entities from database: {}", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered("SELECT `id` FROM `entities`", ids, entities_out_size) - ); - - for (size_t i = 0; i < entities_out_size; ++i) { - entities_out[i] = project->get_entity(ids[i]); - } + IKARUS_TRYRV_OR_FAIL( + , + "unable to fetch project entities from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_many_buffered( + "SELECT `id` FROM `entities`", + ids_out, + ids_out_size + ) + ); } -size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(project, 0); +size_t ikarus_project_get_entity_count( + IkarusProject const * project, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(project, 0); - IKARUS_VTRYRV_OR_FAIL( - auto const ret, - 0, - "unable to fetch project entity count from database: {}", - IkarusErrorInfo_Database_QueryFailed, - project->db->query_one("SELECT COUNT(*) FROM `entities`") - ); + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + 0, + "unable to fetch project entity count from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT COUNT(`id`) FROM `entities`") + ); - return ret; + return ret; } -struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NULL(name, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); +void ikarus_project_get_blueprints( + struct IkarusProject const * project, + int64_t * ids_out, + 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 - // errors. In this case `sqlitecpp::MissingRow` and database related errors. Same for the other functions. - IKARUS_VTRYRV_OR_FAIL( - auto const id, - nullptr, - "unable to find entity in database: {}", - IkarusErrorInfo_Client_InvalidInput, - project->db->query_one("SELECT `id` FROM `entities` WHERE `name` = ?", name) - ); + if (ids_out == 0) { + return; + } - return project->get_entity(id); + IKARUS_TRYRV_OR_FAIL( + , + "unable to fetch project blueprints from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_many_buffered( + "SELECT `id` FROM `blueprints`", + ids_out, + ids_out_size + ) + ); } -struct IkarusProperty * -get_property_by_name(IkarusProject * project, IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NULL(name, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); +size_t ikarus_project_get_blueprint_count( + IkarusProject const * project, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(project, 0); - IKARUS_VTRYRV_OR_FAIL( - auto const id_and_type, - nullptr, - "unable to find property in database: {}", - IkarusErrorInfo_Client_InvalidInput, - project->db->query_one( - "SELECT `id`, `type` FROM `properties` WHERE `name` = ? AND `scope` = ?", - name, - scope->get_id() - ) - ); + IKARUS_VTRYRV_OR_FAIL( + auto const ret, + 0, + "unable to fetch project blueprint count from database: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->query_one("SELECT COUNT(`id`) FROM `blueprints`") + ); - auto const [id, type] = id_and_type; - - return project->get_property(id, type); -} - -IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NULL(name, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); - - IKARUS_VTRYRV_OR_FAIL( - auto const id, - nullptr, - "unable to find blueprint in database: {}", - IkarusErrorInfo_Client_InvalidInput, - project->db->query_one("SELECT `id` FROM `blueprints` WHERE `name` = ?", name) - ); - - return project->get_blueprint(id); + return ret; } diff --git a/src/ikarus/persistence/project.hpp b/src/ikarus/persistence/project.hpp index 1dc69dc..f202ea2 100644 --- a/src/ikarus/persistence/project.hpp +++ b/src/ikarus/persistence/project.hpp @@ -1,58 +1,19 @@ #pragma once +#include +#include #include #include #include -#include -#include +#include +#include +#include -namespace fs = boost::filesystem; - -/// \private struct IkarusProject { -public: - IkarusProject(std::string_view name, std::string_view path, std::unique_ptr && 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 - [[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(this, id)).first->second.get(); - } - - return iter->second.get(); - } - - template - void remove_cached_object(T * object, std::unordered_map> & cache) { - cache.erase(object->id); - } - -public: - std::string name; - std::string_view path; - std::unique_ptr db; - -private: - std::unordered_map> mutable _blueprints; - std::unordered_map> mutable _properties; - std::unordered_map> mutable _entities; + std::string name; + boost::filesystem::path path; + std::unique_ptr db; }; - -constexpr std::string_view DB_PROJECT_NAME_KEY = "PROJECT_NAME"; diff --git a/src/ikarus/values/data.cpp b/src/ikarus/values/data.cpp new file mode 100644 index 0000000..ac893b2 --- /dev/null +++ b/src/ikarus/values/data.cpp @@ -0,0 +1,217 @@ +#include "data.hpp" + +#include + +#include +#include + +#include +#include +#include +#include + +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 { + if (!json.is_object()) { + return cppbase::err(IkarusJsonError{IkarusJsonInvalidTypeError{}}); + } + + IkarusValueData value{}; + + VTRY( + auto type, + deserialize_enum( + json, + "type", + IkarusValueDataType_Primitive, + IkarusValueDataType_Tuple + ) + ); + + switch (type) { + case IkarusValueDataType_Primitive: { + VTRY( + auto primitive, + deserialize_enum( + json, + "primitive", + IkarusValuePrimitiveType_Toggle, + IkarusValuePrimitiveType_Text + ) + ); + + switch (primitive) { + case IkarusValuePrimitiveType_Toggle: { + VTRY(auto data, deserialize_any(json, "data")); + + value.variant = IkarusValueDataPrimitiveToggle{data}; + break; + } + case IkarusValuePrimitiveType_Number: { + VTRY(auto data, deserialize_any(json, "data")); + + value.variant = IkarusValueDataPrimitiveNumber{data}; + break; + } + case IkarusValuePrimitiveType_Text: { + VTRY(auto data, deserialize_any(json, "data")); + + value.variant = IkarusValueDataPrimitiveString{data}; + break; + } + } + + break; + } + case IkarusValueDataType_List: { + std::vector> values_data{}; + VTRY( + auto data_json, + deserialize_any>(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(std::move(value_data)) + ); + } + + value.variant = IkarusValueDataList{values_data}; + break; + } + case IkarusValueDataType_Map: { + std::vector, + cppbase::owning_ptr>> + map_data{}; + + VTRY( + auto map_data_json, + deserialize_any>(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(key), + cppbase::make_owning(value) + ); + } + + value.variant = IkarusValueDataMap{map_data}; + break; + } + case IkarusValueDataType_Tuple: { + std::vector> values_data{}; + + VTRY( + auto values_json, + deserialize_any>(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(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>(); + }, + [&](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>(); + }, + [&](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>(); + } + }, + value.variant + ); +} diff --git a/src/ikarus/values/data.hpp b/src/ikarus/values/data.hpp new file mode 100644 index 0000000..d4e92ad --- /dev/null +++ b/src/ikarus/values/data.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +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> values; +}; + +struct IkarusValueDataMap { + std::vector, + cppbase::owning_ptr>> + values; +}; + +struct IkarusValueDataTuple { + std::vector> values; +}; + +struct IkarusValueData { + using IkarusValueDataVariant = std::variant< + IkarusValueDataPrimitive, + IkarusValueDataList, + IkarusValueDataMap, + IkarusValueDataTuple>; + + static auto from_json(nlohmann::json const & json) + -> cppbase::Result; + static auto to_json(nlohmann::json & json, IkarusValueData const & value) + -> void; + + IkarusValueDataVariant variant; +}; + +auto get_primitive_type(IkarusValueDataPrimitive const & primitive) + -> IkarusValuePrimitiveType; diff --git a/src/ikarus/values/entity_property_value.cpp b/src/ikarus/values/entity_property_value.cpp deleted file mode 100644 index 165383d..0000000 --- a/src/ikarus/values/entity_property_value.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "entity_property_value.hpp" - -#include - -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; -} diff --git a/src/ikarus/values/entity_property_value.hpp b/src/ikarus/values/entity_property_value.hpp deleted file mode 100644 index 6f70f09..0000000 --- a/src/ikarus/values/entity_property_value.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/src/ikarus/values/errors.hpp b/src/ikarus/values/errors.hpp new file mode 100644 index 0000000..ed752af --- /dev/null +++ b/src/ikarus/values/errors.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +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; diff --git a/src/ikarus/values/number_value.cpp b/src/ikarus/values/number_value.cpp deleted file mode 100644 index 41fba42..0000000 --- a/src/ikarus/values/number_value.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "ikarus/values/number_value.h" - -#include - -#include -#include - -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); -} diff --git a/src/ikarus/values/number_value.hpp b/src/ikarus/values/number_value.hpp deleted file mode 100644 index bd4bec5..0000000 --- a/src/ikarus/values/number_value.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include - -#include - -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> data{}; -}; diff --git a/src/ikarus/values/schema.cpp b/src/ikarus/values/schema.cpp new file mode 100644 index 0000000..e6cbd24 --- /dev/null +++ b/src/ikarus/values/schema.cpp @@ -0,0 +1,177 @@ +#include "schema.hpp" + +#include + +#include +#include +#include + +auto IkarusValueSchema::from_json(nlohmann::json const & json) + -> cppbase::Result { + if (!json.is_object()) { + return cppbase::err( + IkarusValueSchemaParseError{ + IkarusJsonError{IkarusJsonInvalidTypeError{}} + } + ); + } + + IkarusValueSchema schema{}; + + VTRY( + auto type, + deserialize_enum( + json, + "type", + IkarusValueSchemaType_Primitive, + IkarusValueSchemaType_Tuple + ) + ); + + switch (type) { + case IkarusValueSchemaType_Primitive: { + VTRY( + auto primitive, + deserialize_enum( + 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(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(std::move(key_schema)), + cppbase::make_owning(std::move(value_schema)) + }; + break; + } + case IkarusValueSchemaType_Tuple: { + VTRY( + auto sub_schemas_json, + deserialize_any>(json, "schemas") + ); + + std::vector> 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(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 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 + ); +} diff --git a/src/ikarus/values/schema.hpp b/src/ikarus/values/schema.hpp new file mode 100644 index 0000000..4f4256c --- /dev/null +++ b/src/ikarus/values/schema.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +struct IkarusValue; +struct IkarusValueData; +struct IkarusValueSchema; + +struct IkarusValueSchemaPrimitive { + IkarusValuePrimitiveType type; +}; + +struct IkarusValueSchemaList { + cppbase::owning_ptr sub_schema; +}; + +struct IkarusValueSchemaMap { + cppbase::owning_ptr key_schema; + cppbase::owning_ptr value_schema; +}; + +struct IkarusValueSchemaTuple { + std::vector> sub_schemas; +}; + +struct IkarusValueSchema { + using IkarusValueSchemaVariant = std::variant< + IkarusValueSchemaPrimitive, + IkarusValueSchemaList, + IkarusValueSchemaMap, + IkarusValueSchemaTuple>; + + static auto from_json(nlohmann::json const & json) + -> cppbase::Result; + static auto to_json(nlohmann::json & json, IkarusValueSchema const & value) + -> void; + + auto validate(IkarusValueData const & data) const -> bool; + + IkarusValueSchemaVariant variant; +}; diff --git a/src/ikarus/values/shared.hpp b/src/ikarus/values/shared.hpp new file mode 100644 index 0000000..a4d50ac --- /dev/null +++ b/src/ikarus/values/shared.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "errors.hpp" + +#include + +#include +#include + +inline auto get_key(nlohmann::json const & json, std::string_view key) + -> cppbase:: + Result { + auto iter = json.find(key); + if (iter == json.end()) { + return cppbase::err(IkarusJsonMissingKeyError{}); + } + + return cppbase::ok(iter); +} + +template +auto serialize_enum(nlohmann::json & json, std::string_view key, E value) + -> void { + json[key] = static_cast>(value); +} + +template + requires std::is_integral_v> +auto deserialize_enum( + nlohmann::json const & json, + std::string_view key, + E min, + E max +) -> cppbase::Result { + VTRY(auto iter, get_key(json, key)); + + if (!iter->is_number_integer()) { + return cppbase::err(IkarusJsonError{}); + } + + auto value = iter->get>(); + + if (value < static_cast>(min) || + value > static_cast>(max)) { + return cppbase::err(IkarusJsonError{}); + } + + return cppbase::ok(static_cast(value)); +} + +template +auto deserialize_any(nlohmann::json const & json, std::string_view key) + -> cppbase::Result { + VTRY(auto iter, get_key(json, key)); + + try { + return cppbase::ok(iter->get()); + } 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{}); + } +} diff --git a/src/ikarus/values/text_value.cpp b/src/ikarus/values/text_value.cpp deleted file mode 100644 index 9400f2e..0000000 --- a/src/ikarus/values/text_value.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "ikarus/values/text_value.h" - -#include -#include - -#include -#include - -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); -} diff --git a/src/ikarus/values/text_value.hpp b/src/ikarus/values/text_value.hpp deleted file mode 100644 index a1e36ea..0000000 --- a/src/ikarus/values/text_value.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include - -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> data{}; -}; diff --git a/src/ikarus/values/toggle_value.cpp b/src/ikarus/values/toggle_value.cpp deleted file mode 100644 index fb76e68..0000000 --- a/src/ikarus/values/toggle_value.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "ikarus/values/toggle_value.h" - -#include -#include - -#include -#include - -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); -} diff --git a/src/ikarus/values/toggle_value.hpp b/src/ikarus/values/toggle_value.hpp deleted file mode 100644 index fade2c1..0000000 --- a/src/ikarus/values/toggle_value.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include - -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> data{}; -}; diff --git a/src/ikarus/values/value.cpp b/src/ikarus/values/value.cpp index de57e26..07c0c24 100644 --- a/src/ikarus/values/value.cpp +++ b/src/ikarus/values/value.cpp @@ -1,134 +1,58 @@ -#include "ikarus/values/value.h" +#include "value.hpp" -#include +#include +#include +#include +#include +#include +#include -#include -#include +auto IkarusValue::from_json(nlohmann::json const & json) + -> cppbase::Result { + if (!json.is_object()) { + return cppbase::err(IkarusJsonError{IkarusJsonInvalidTypeError{}}); + } -// required for header-only inclusion -#include + IkarusValue value{}; -#include -#include -#include -#include -#include -#include + VTRY(value.schema, IkarusValueSchema::from_json(json["schema"])); + VTRY(value.data, IkarusValueData::from_json(json["data"])); -IkarusValue::IkarusValue(Data data, IkarusValueCardinality cardinality): - data{data}, - cardinality{cardinality} {} + if (!value.schema.validate(value.data)) { + return cppbase::err(IkarusValueParseErrorDataSchemaMismatch{}); + } -cppbase::Result IkarusValue::from_json(boost::json::value json) { - 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]() -> cppbase::Result { - 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>(*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()(); - } - case IkarusValueType_Number: { - return create_value.operator()(); - } - case IkarusValueType_Text: { - return create_value.operator()(); - } - default: { - return cppbase::err(FromJsonError{}); - } - } - } + return cppbase::ok(std::move(value)); } -boost::json::value IkarusValue::to_json() const { - auto type = std::visit( - cppbase::overloaded{ - []([[maybe_unused]] IkarusToggleValue const * value) { return IkarusValueType_Toggle; }, - []([[maybe_unused]] IkarusNumberValue const * value) { return IkarusValueType_Number; }, - []([[maybe_unused]] IkarusTextValue const * value) { return IkarusValueType_Text; } - }, - data - ); - - auto data_json = std::visit( - [](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} - }; +auto IkarusValue::to_json(nlohmann::json & json, IkarusValue const & value) + -> void { + IkarusValueSchema::to_json(json["schema"], value.schema); + IkarusValueData::to_json(json["data"], value.data); } -void ikarus_value_visit( - IkarusValue * value, - void (*toggle_visitor)(IkarusToggleValue *, void *), - void (*number_visitor)(IkarusNumberValue *, void *), - void (*text_visitor)(IkarusTextValue *, void *), - void * data -) { - std::visit( - cppbase::overloaded{ - [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); } - }, - value->data - ); +auto IkarusValues::from_json(nlohmann::json const & json) + -> cppbase::Result { + IkarusValues values{}; + + for (auto const & json_entry : json) { + VTRY(auto name_json, get_key(json_entry, "name")); + + if (!name_json->is_string()) { + return cppbase::err(IkarusJsonInvalidTypeError{}); + } + + VTRY(auto value_json, get_key(json_entry, "value")); + + VTRY(auto value, IkarusValue::from_json(*value_json)); + + values.values.emplace_back( + std::make_pair( + std::move(name_json->get()), + std::move(value) + ) + ); + } + + return cppbase::ok(std::move(values)); } diff --git a/src/ikarus/values/value.hpp b/src/ikarus/values/value.hpp index 07e92fc..47b3600 100644 --- a/src/ikarus/values/value.hpp +++ b/src/ikarus/values/value.hpp @@ -1,98 +1,28 @@ #pragma once -#include +#include #include -#include -#include -#include - -constexpr auto IKARUS_VALUE_JSON_TYPE_KEY = "type"; -constexpr auto IKARUS_VALUE_JSON_CARDINALITY_KEY = "card"; -constexpr auto IKARUS_VALUE_JSON_DATA_KEY = "data"; +#include +#include +#include struct IkarusValue { -public: - using Data = std::variant; - constexpr static auto SMALL_VEC_VALUE_SIZE = 8; + IkarusValueSchema schema; + IkarusValueData data; -public: - explicit IkarusValue(Data data, IkarusValueCardinality cardinality); - - IkarusValue(IkarusValue const &) = default; - IkarusValue(IkarusValue &&) noexcept = default; - - IkarusValue & operator=(IkarusValue const &) = default; - IkarusValue & operator=(IkarusValue &&) noexcept = default; - - virtual ~IkarusValue() = default; - -public: - struct FromJsonError {}; - - [[nodiscard]] static cppbase::Result from_json(boost::json::value json); - [[nodiscard]] boost::json::value to_json() const; - -public: - Data data; - IkarusValueCardinality cardinality; + static auto from_json(nlohmann::json const & json) + -> cppbase::Result; + static auto to_json(nlohmann::json & json, IkarusValue const & value) + -> void; }; -template<> -struct fmt::formatter { - template - constexpr static auto parse(FormatParseContext & ctx) { - return ctx.end(); - } +struct IkarusValues { + static auto from_json(nlohmann::json const & json) + -> cppbase::Result; + static auto to_json(nlohmann::json & json, IkarusValues const & values) + -> void; - constexpr static auto format([[maybe_unused]] IkarusValue::FromJsonError const & error, fmt::format_context & ctx) { - return fmt::format_to(ctx.out(), "unable to parse ikarus value JSON"); - } + std::vector> values; }; - -template -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(query, std::forward(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( \ - "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); diff --git a/src/ikarus/values/value_base.hpp b/src/ikarus/values/value_base.hpp deleted file mode 100644 index 5fa18ae..0000000 --- a/src/ikarus/values/value_base.hpp +++ /dev/null @@ -1,126 +0,0 @@ -#pragma once - -#include - -#include - -#include - -template -typename V::DataType const * ikarus_value_data_base_get(V * value, size_t idx) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - return &(*data)[idx]; - } - - return nullptr; -} - -template -size_t ikarus_value_data_base_get_size(V const * value) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - return data->size(); - } - - return 0; -} - -template -void ikarus_value_data_base_set(V * value, size_t idx, typename V::DataType new_data) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - (*data)[idx] = new_data; - } -} - -template -void ikarus_value_data_base_remove(V * value, size_t idx) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - data->erase(data->begin() + idx); - } -} - -template -void ikarus_value_data_base_insert(V * value, size_t idx, typename V::DataType new_data) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - data->insert(data->begin() + idx, new_data); - } -} - -template -void ikarus_value_data_base_clear(V * value) { - if (auto * data = boost::variant2::get_if>( - &value->data - ); - data != nullptr) { - data->clear(); - } -} - -template -bool ikarus_value_data_base_is_undefined(V const * value) { - return boost::variant2::holds_alternative(value->data); -} - -template -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{}; - } -} - -template 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(transformer)), ", ") - ); - - return buffer.data(); - } - }, - value->data - ); -} - -template -bool ikarus_value_data_base_is_equal(V const * lhs, V const * rhs) { - return lhs->data == rhs->data; -} - -template -V * ikarus_value_data_base_copy(V const * value) { - return new V{*value}; -} - -template -struct IkarusValueData * ikarus_value_data_base_to_value(V * value) { - return static_cast(value); -} - -template -struct IkarusValueData const * ikarus_value_data_base_to_value_data_const(V const * value) { - return static_cast(value); -} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/tools/cmake/toolchains/mac.cmake b/tools/cmake/toolchains/mac.cmake new file mode 100644 index 0000000..b7120cd --- /dev/null +++ b/tools/cmake/toolchains/mac.cmake @@ -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") diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 611db2e..5b8f409 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -1,3 +1,8 @@ # order is important here since sqlitecpp otherwise duplicates cppbase add_subdirectory(cppbase) add_subdirectory(sqlitecpp) +add_subdirectory(json) + +if (LIBIKARUS_ENABLE_TESTS) + add_subdirectory(catch2) +endif () diff --git a/vendor/catch2 b/vendor/catch2 new file mode 160000 index 0000000..f5cee49 --- /dev/null +++ b/vendor/catch2 @@ -0,0 +1 @@ +Subproject commit f5cee49c71e7c0e805695b03e8af533783789762 diff --git a/vendor/json b/vendor/json new file mode 160000 index 0000000..6057b31 --- /dev/null +++ b/vendor/json @@ -0,0 +1 @@ +Subproject commit 6057b31df7fe0b958d1795defc108d1f26e26441 diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 32fa3c1..8edc04b 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 32fa3c1a9beae1a2c49fdd08cc659ea6153da666 +Subproject commit 8edc04b73bdee4b487ca59abd68615fa66c7948b From bdee0b4c8ecd47cf8402a9af8da96df32ae65fc4 Mon Sep 17 00:00:00 2001 From: Folling Date: Wed, 1 Jan 2025 13:55:13 +0100 Subject: [PATCH 153/166] update vendor libraries Signed-off-by: Folling --- .gitmodules | 4 ++-- vendor/cppbase | 2 +- vendor/doxygen-awesome-css | 2 +- vendor/sqlitecpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index 94e3d41..029631e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,13 +3,13 @@ url = git@github.com:catchorg/Catch2.git [submodule "vendor/sqlitecpp"] path = vendor/sqlitecpp - url = ssh://git@git.rewritesarebliss.com:16658/Folling/sqlitecpp.git + url = ssh://git@git.foll.ing:59575/folling/sqlitecpp.git [submodule "vendor/doxygen-awesome-css"] path = vendor/doxygen-awesome-css url = git@github.com:jothepro/doxygen-awesome-css.git [submodule "vendor/cppbase"] path = vendor/cppbase - url = ssh://git@git.rewritesarebliss.com:16658/Folling/cppbase.git + url = ssh://git@git.foll.ing:59575/folling/cppbase.git [submodule "vendor/json"] path = vendor/json url = git@github.com:nlohmann/json.git diff --git a/vendor/cppbase b/vendor/cppbase index 6bf80cf..e7741e0 160000 --- a/vendor/cppbase +++ b/vendor/cppbase @@ -1 +1 @@ -Subproject commit 6bf80cf9d5ff54ab300f7e88a8cb9996f441dae6 +Subproject commit e7741e0e60f380f8b473c0881f3d8b4f23d0df91 diff --git a/vendor/doxygen-awesome-css b/vendor/doxygen-awesome-css index 00a52f6..af1d903 160000 --- a/vendor/doxygen-awesome-css +++ b/vendor/doxygen-awesome-css @@ -1 +1 @@ -Subproject commit 00a52f6c74065ffbd836cbd791ddfe8edf2836b8 +Subproject commit af1d9030b3ffa7b483fa9997a7272fb12af6af4c diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 8edc04b..5bfd32f 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 8edc04b73bdee4b487ca59abd68615fa66c7948b +Subproject commit 5bfd32f66164ed3d2c28003139fc5f76ef5e206d From 921d251c961d7b191b6c4acafd289874d7201925 Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 2 Jan 2025 09:39:08 +0100 Subject: [PATCH 154/166] finalize entity functions Signed-off-by: Folling --- .clang-format | 2 +- include/ikarus/objects/entity.h | 41 +- src/ikarus/errors.hpp | 11 +- src/ikarus/objects/blueprint.cpp | 4 + src/ikarus/objects/blueprint.hpp | 16 +- src/ikarus/objects/entity.cpp | 385 ++++++++++++++++-- src/ikarus/objects/entity.hpp | 10 +- src/ikarus/objects/property.cpp | 9 +- src/ikarus/objects/property.hpp | 16 +- .../migrations/m0_initial_layout.sql | 5 +- src/ikarus/persistence/project.cpp | 18 +- src/ikarus/values/data.cpp | 21 +- src/ikarus/values/data.hpp | 3 +- src/ikarus/values/errors.hpp | 81 ++++ src/ikarus/values/schema.cpp | 30 +- src/ikarus/values/schema.hpp | 3 +- src/ikarus/values/value.cpp | 37 +- src/ikarus/values/value.hpp | 12 +- 18 files changed, 523 insertions(+), 181 deletions(-) diff --git a/.clang-format b/.clang-format index 6d3560a..5456c6f 100644 --- a/.clang-format +++ b/.clang-format @@ -64,7 +64,7 @@ BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon -BreakStringLiterals: true +BreakStringLiterals: false ColumnLimit: 80 CommentPragmas: '^\\.+' diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 0481da7..172bad1 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -181,8 +181,6 @@ IKA_API void ikarus_entity_link_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. @@ -203,17 +201,27 @@ IKA_API void ikarus_entity_unlink_blueprint( IkarusErrorData * error_out ); +/// \brief Struct for an entity value. +struct IkarusEntityValue { + /// \brief The name of the value. + char const * name; + /// \brief The value in json format. \see value.h + char const * value; +}; + /// \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 size_out An out parameter for the number of values in the returned array +/// or undefined if an error occurs. /// \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); +/// \return The values or null if an error occurs. +IKA_API IkarusEntityValue * ikarus_entity_get_values( + IkarusEntity * entity, + size_t * size_out, + IkarusErrorData * error_out +); /// \brief Gets a value of an entity. /// \param entity The entity to get the value of. @@ -279,16 +287,25 @@ IKA_API void ikarus_entity_delete_value( IkarusErrorData * error_out ); +/// \brief Struct for an entity property value. +struct IkarusEntityPropertyValue { + /// \brief The property. + struct IkarusProperty * property; + /// \brief The value in json format. \see value.h + char const * value; +}; + /// \brief Gets the property values of an entity. /// \param entity The entity to get the property values of. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param size_out An out parameter for the number of property values in the +/// returned array or undefined if an error occurs. /// \param error_out \see errors.h -/// \return The property values, in msgpack format of or null if an error occurs. -/// The format is a map of property pointers (as integers) to values. \see -/// value.h -IKA_API char const * ikarus_entity_get_property_values( +/// \return The property values, or null if an error occurs. +IKA_API IkarusEntityPropertyValue * ikarus_entity_get_property_values( IkarusEntity * entity, + size_t * size_out, IkarusErrorData * error_out ); diff --git a/src/ikarus/errors.hpp b/src/ikarus/errors.hpp index 55b3e8f..a9daa9d 100644 --- a/src/ikarus/errors.hpp +++ b/src/ikarus/errors.hpp @@ -11,6 +11,8 @@ void safe_strcpy( size_t const dest_size ); +#define IKARUS_VOID_RETURN + #define IKARUS_SET_ERROR(msg, err_info) \ if (error_out != nullptr) { \ safe_strcpy( \ @@ -140,9 +142,9 @@ void safe_strcpy( IkarusErrorInfo_Client_InvalidInput \ ); -#define IKARUS_FAIL_IF_NOT_EXIST(object, ret) \ +#define IKARUS_FAIL_IF_NOT_EXIST_IMPL(exists_name, object, ret) \ IKARUS_VTRYRV_OR_FAIL( \ - auto exists, \ + auto exists_name, \ ret, \ fmt::format( \ "failed to check if {} exists", \ @@ -159,7 +161,7 @@ void safe_strcpy( ); \ \ IKARUS_FAIL_IF( \ - !exists, \ + !exists_name, \ ret, \ fmt::format( \ "{} doesn't exist", \ @@ -167,3 +169,6 @@ void safe_strcpy( ), \ IkarusErrorInfo_Client_NonExistent \ ) + +#define IKARUS_FAIL_IF_NOT_EXIST(object, ret) \ + IKARUS_FAIL_IF_NOT_EXIST_IMPL(CPPBASE_UNIQUE_NAME(exists), object, ret) diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index c25961f..553bbe2 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -3,3 +3,7 @@ #include #include #include + +IkarusBlueprint::IkarusBlueprint(struct IkarusProject * project, int64_t id): + project{project}, + id{id} {} diff --git a/src/ikarus/objects/blueprint.hpp b/src/ikarus/objects/blueprint.hpp index b56143f..64cd9ea 100644 --- a/src/ikarus/objects/blueprint.hpp +++ b/src/ikarus/objects/blueprint.hpp @@ -3,21 +3,11 @@ #include struct IkarusBlueprint { - consteval static inline auto OBJECT_NAME() -> std::string_view { - return "blueprint"; - } + constinit static inline auto object_name = "blueprint"; + constinit static inline auto table_name = "blueprints"; - consteval static inline auto TABLE_NAME() -> std::string_view { - return "blueprints"; - } - - IkarusBlueprint( - struct IkarusProject * project, - int64_t id, - std::string_view name - ); + IkarusBlueprint(struct IkarusProject * project, int64_t id); struct IkarusProject * project; int64_t id; - std::string name; }; diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 47f1f90..5abd085 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -1,18 +1,15 @@ #include "entity.hpp" +#include + #include #include #include #include -IkarusEntity::IkarusEntity( - struct IkarusProject * project, - int64_t id, - std::string_view name -): +IkarusEntity::IkarusEntity(struct IkarusProject * project, int64_t id): project{project}, - id{id}, - name{name} {} + id{id} {} IkarusEntity * ikarus_entity_create( struct IkarusProject * project, @@ -31,7 +28,7 @@ IkarusEntity * ikarus_entity_create( ); auto const id = project->db->last_insert_rowid(); - return new IkarusEntity{project, id, name}; + return new IkarusEntity{project, id}; } void ikarus_entity_delete( @@ -39,12 +36,11 @@ void ikarus_entity_delete( IkarusEntityDeleteFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, ); - IKARUS_FAIL_IF_NOT_EXIST(entity, ); - IKARUS_FAIL_IF_NULL(entity->project, ); + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); IKARUS_TRYRV_OR_FAIL( - , + IKARUS_VOID_RETURN, "failed to delete entity: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db @@ -61,7 +57,6 @@ IkarusEntity * ikarus_entity_copy( ) { 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, @@ -72,34 +67,28 @@ IkarusEntity * ikarus_entity_copy( [entity](auto * db) -> cppbase::Result { TRY(entity->project->db->execute( - "INSERT INTO `entities`(`name`) VALUES(?)", - entity->name.data() + "INSERT INTO `entities`(`name`) " + "SELECT `name` FROM `entities` WHERE `id` = ?", + entity->id )); TRY(entity->project->db->execute( - "INSERT INTO `entity_values`(`entity`, `name`, `value`)" - " SELECT ?1, `name`, `value` FROM `entity_values` WHERE " + "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` " + "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` " + "INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) " + "SELECT ?1, `property`, `value` FROM `entity_property_values` " "WHERE `entity` = ?1", entity->id )) @@ -109,13 +98,13 @@ IkarusEntity * ikarus_entity_copy( ) ); - return new IkarusEntity{entity->project, id, entity->name}; + return new IkarusEntity{entity->project, id}; } IkarusProject * ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NULL(entity->project, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); return entity->project; } @@ -123,8 +112,20 @@ ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) { char const * ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); - return entity->name.data(); + IKARUS_VTRYRV_OR_FAIL( + auto name, + nullptr, + "failed to get name for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT `name` FROM `entities` WHERE `id` = ?", + entity->id + ) + ); + + return name; } void ikarus_entity_set_name( @@ -133,11 +134,12 @@ void ikarus_entity_set_name( IkarusEntitySetNameFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, ); - IKARUS_FAIL_IF_NAME_INVALID(name, ); + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN); IKARUS_TRYRV_OR_FAIL( - , + IKARUS_VOID_RETURN, "failed to set name for entity: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->execute( @@ -146,8 +148,152 @@ void ikarus_entity_set_name( entity->id ) ); +} - entity->name = name; +struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( + IkarusEntity * entity, + size_t * size_out, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); + IKARUS_FAIL_IF_NULL(size_out, nullptr); + + auto count = ikarus_entity_get_linked_blueprints_count(entity, error_out); + IKARUS_FAIL_IF_ERROR(nullptr); + + std::int64_t ids[count]; + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to get linked blueprints for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_many_buffered( + "SELECT `blueprint` FROM `entity_blueprint_links` WHERE `entity` = ?", + ids, + count, + entity->id + ) + ); + + auto blueprints = new IkarusBlueprint *[count]; + + std::transform(ids, ids + count, blueprints, [entity](auto id) { + return new IkarusBlueprint{entity->project, id}; + }); + + if (size_out) { + *size_out = count; + } + + return blueprints; +} + +size_t ikarus_entity_get_linked_blueprints_count( + IkarusEntity * entity, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, 0); + IKARUS_FAIL_IF_NOT_EXIST(entity, 0); + + IKARUS_VTRYRV_OR_FAIL( + auto count, + 0, + "failed to get linked blueprints count for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", + entity->id + ) + ); + + return count; +} + +void ikarus_entity_link_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusEntityLinkBlueprintFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(blueprint, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(blueprint, IKARUS_VOID_RETURN); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to link blueprint to entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) VALUES(?, ?)", + entity->id, + blueprint->id + ) + ); +} + +void ikarus_entity_unlink_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusEntityUnlinkBlueprintFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(blueprint, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(blueprint, IKARUS_VOID_RETURN); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to unlink blueprint from entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "DELETE FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?", + entity->id, + blueprint->id + ) + ); +} + +IkarusEntityValue * ikarus_entity_get_values( + IkarusEntity * entity, + size_t * size_out, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto values_plain, + nullptr, + "failed to get values for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_many( + "SELECT `name`, `value` FROM `entity_values` WHERE `entity` = ?", + entity->id + ) + ); + + IkarusEntityValue * values = new IkarusEntityValue[values_plain.size()]; + + std::transform( + std::cbegin(values_plain), + std::cend(values_plain), + values, + [](auto const & tuple) { + return IkarusEntityValue{ + tuple.template get<0>(), + tuple.template get<1>() + }; + } + ); + + if (size_out) { + *size_out = values_plain.size(); + } + + return values; } char const * ikarus_entity_get_value( @@ -156,7 +302,22 @@ char const * ikarus_entity_get_value( IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); IKARUS_FAIL_IF_NULL(name, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto value, + nullptr, + "failed to get value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT `value` FROM `entity_values` WHERE `entity` = ? AND `name` = ?", + entity->id, + name + ) + ); + + return value; } void ikarus_entity_set_value( @@ -165,7 +326,155 @@ void ikarus_entity_set_value( char const * value, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, ); - IKARUS_FAIL_IF_NULL(name, ); - IKARUS_FAIL_IF_NULL(value, ); + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + + // parsing from & to here to ensure values are valid JSON & formatted + // uniformly + IKARUS_VTRYRV_OR_FAIL( + auto value_parsed, + IKARUS_VOID_RETURN, + "cannot parse value as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValue::from_json(value) + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "INSERT INTO `entity_values`(`entity`, `name`, `value`) VALUES(?, ?, ?) " + "ON CONFLICT(`entity`, `name`) DO UPDATE SET `value` = excluded.`value`", + entity->id, + name, + IkarusValue::to_json(value_parsed).dump() + ) + ); +} + +void ikarus_entity_delete_value( + IkarusEntity * entity, + char const * name, + IkarusEntityDeleteValueFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to delete value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "DELETE FROM `entity_values` WHERE `entity` = ? AND `name` = ?", + entity->id, + name + ) + ); +} + +IkarusEntityPropertyValue * ikarus_entity_get_property_values( + IkarusEntity * entity, + size_t * size_out, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto values_plain, + nullptr, + "failed to get property values for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_many( + "SELECT `property`, `value` FROM `entity_property_values` WHERE `entity` = ?", + entity->id + ) + ); + + IkarusEntityPropertyValue * values = + new IkarusEntityPropertyValue[values_plain.size()]; + std::transform( + std::cbegin(values_plain), + std::cend(values_plain), + values, + [entity](auto const & tuple) { + return IkarusEntityPropertyValue{ + new IkarusProperty{entity->project, tuple.template get<0>()}, + tuple.template get<1>() + }; + } + ); + + if (size_out) { + *size_out = values_plain.size(); + } + + return values; +} + +char const * ikarus_entity_get_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); + IKARUS_FAIL_IF_NULL(property, nullptr); + IKARUS_FAIL_IF_NOT_EXIST(property, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto value, + nullptr, + "failed to get property value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT `value` FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?", + entity->id, + property->id + ) + ); + + return value; +} + +void ikarus_entity_set_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + char const * value, + IkarusEntitySetPropertyValueFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(property, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NOT_EXIST(property, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + + // parsing from & to here to ensure values are valid JSON & formatted + // uniformly + IKARUS_VTRYRV_OR_FAIL( + auto value_parsed, + IKARUS_VOID_RETURN, + "cannot parse value as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValue::from_json(value) + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set property value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "INSERT INTO `entity_property_values`(`entity`, `property`, `value`) VALUES(?, ?, ?) " + "ON CONFLICT(`entity`, `property`) DO UPDATE SET `value` = excluded.`value`", + entity->id, + property->id, + IkarusValue::to_json(value_parsed).dump() + ) + ); } diff --git a/src/ikarus/objects/entity.hpp b/src/ikarus/objects/entity.hpp index 90a445e..449d69e 100644 --- a/src/ikarus/objects/entity.hpp +++ b/src/ikarus/objects/entity.hpp @@ -9,16 +9,8 @@ struct IkarusEntity { constinit static inline auto object_name = "entity"; constinit static inline auto table_name = "entities"; - IkarusEntity( - struct IkarusProject * project, - int64_t id, - std::string_view name - ); + IkarusEntity(struct IkarusProject * project, int64_t id); struct IkarusProject * project; int64_t id; - std::string name; - - std::vector> values_ordered; - std::unordered_map values; }; diff --git a/src/ikarus/objects/property.cpp b/src/ikarus/objects/property.cpp index d096b18..43fac96 100644 --- a/src/ikarus/objects/property.cpp +++ b/src/ikarus/objects/property.cpp @@ -5,11 +5,6 @@ #include #include -IkarusProperty::IkarusProperty( - struct IkarusProject * project, - int64_t id, - std::string_view name -): +IkarusProperty::IkarusProperty(struct IkarusProject * project, int64_t id): project{project}, - id{id}, - name{name} {} + id{id} {} diff --git a/src/ikarus/objects/property.hpp b/src/ikarus/objects/property.hpp index 29143fb..32bbbc9 100644 --- a/src/ikarus/objects/property.hpp +++ b/src/ikarus/objects/property.hpp @@ -3,21 +3,11 @@ #include struct IkarusProperty { - consteval static inline auto OBJECT_NAME() -> std::string_view { - return "property"; - } + constinit static inline auto object_name = "property"; + constinit static inline auto table_name = "properties"; - consteval static inline auto TABLE_NAME() -> std::string_view { - return "properties"; - } - - IkarusProperty( - struct IkarusProject * project, - int64_t id, - std::string_view name - ); + IkarusProperty(struct IkarusProject * project, int64_t id); struct IkarusProject * project; int64_t id; - std::string name; }; diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index 8b263d7..e83b41a 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -6,7 +6,6 @@ CREATE TABLE `entities` CREATE TABLE `entity_values` ( - `id` INTEGER PRIMARY KEY, `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `name` TEXT NOT NULL, `value` TEXT NOT NULL, @@ -36,12 +35,12 @@ CREATE TABLE `entity_blueprint_links` `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE, - PRIMARY KEY (`entity`, `blueprint`) + PRIMARY KEY (`entity`, `blueprint`), + FOREIGN KEY (`entity`, `blueprint`) REFERENCES `entity_blueprint_links` (`entity`, `blueprint`) ON DELETE CASCADE ) STRICT; CREATE TABLE `entity_property_values` ( - `id` INTEGER PRIMARY KEY, `entity` INTEGER NOT NULL REFERENCES `entities` (`id`) ON DELETE CASCADE, `property` INTEGER NOT NULL REFERENCES `properties` (`id`) ON DELETE CASCADE, `value` TEXT NOT NULL, diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 300bf86..75c8825 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -232,18 +232,18 @@ void ikarus_project_set_name( char const * new_name, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, ); - IKARUS_FAIL_IF_NULL(new_name, ); + IKARUS_FAIL_IF_NULL(project, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(new_name, IKARUS_VOID_RETURN); IKARUS_FAIL_IF( cppbase::is_empty_or_blank(new_name), - , + IKARUS_VOID_RETURN, "name must not be empty", IkarusErrorInfo_Client_InvalidInput ); IKARUS_TRYRV_OR_FAIL( - , + IKARUS_VOID_RETURN, "failed to update project name: {}", IkarusErrorInfo_Database_QueryFailed, project->db->execute( @@ -269,15 +269,15 @@ void ikarus_project_get_entities( uint64_t ids_out_size, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, ); - IKARUS_FAIL_IF_NULL(ids_out, ); + IKARUS_FAIL_IF_NULL(project, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(ids_out, IKARUS_VOID_RETURN); if (ids_out == 0) { return; } IKARUS_TRYRV_OR_FAIL( - , + IKARUS_VOID_RETURN, "unable to fetch project entities from database: {}", IkarusErrorInfo_Database_QueryFailed, project->db->query_many_buffered( @@ -311,8 +311,8 @@ void ikarus_project_get_blueprints( uint64_t ids_out_size, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(project, ); - IKARUS_FAIL_IF_NULL(ids_out, ); + IKARUS_FAIL_IF_NULL(project, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(ids_out, IKARUS_VOID_RETURN); if (ids_out == 0) { return; diff --git a/src/ikarus/values/data.cpp b/src/ikarus/values/data.cpp index ac893b2..f7f6845 100644 --- a/src/ikarus/values/data.cpp +++ b/src/ikarus/values/data.cpp @@ -152,10 +152,9 @@ auto IkarusValueData::from_json(nlohmann::json const & json) return cppbase::ok(value); } -auto IkarusValueData::to_json( - nlohmann::json & json, - IkarusValueData const & value -) -> void { +auto IkarusValueData::to_json(IkarusValueData const & value) -> nlohmann::json { + nlohmann::json json = nlohmann::json::object(); + std::visit( cppbase::overloaded{ [&](IkarusValueDataPrimitive const & primitive) { @@ -184,9 +183,7 @@ auto IkarusValueData::to_json( json["type"] = IkarusValueDataType_List; json["data"] = list.values | std::views::transform([](auto const & data) { - nlohmann::json j; - IkarusValueData::to_json(j, *data); - return j; + return IkarusValueData::to_json(*data); }) | std::ranges::to>(); }, @@ -195,8 +192,8 @@ auto IkarusValueData::to_json( 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); + j["key"] = IkarusValueData::to_json(*pair.first); + j["value"] = IkarusValueData::to_json(*pair.second); return j; }) | std::ranges::to>(); @@ -205,13 +202,13 @@ auto IkarusValueData::to_json( json["type"] = IkarusValueDataType_Tuple; json["data"] = tuple.values | std::views::transform([](auto const & data) { - nlohmann::json j; - IkarusValueData::to_json(j, *data); - return j; + return IkarusValueData::to_json(*data); }) | std::ranges::to>(); } }, value.variant ); + + return json; } diff --git a/src/ikarus/values/data.hpp b/src/ikarus/values/data.hpp index d4e92ad..1c9b3d8 100644 --- a/src/ikarus/values/data.hpp +++ b/src/ikarus/values/data.hpp @@ -55,8 +55,7 @@ struct IkarusValueData { static auto from_json(nlohmann::json const & json) -> cppbase::Result; - static auto to_json(nlohmann::json & json, IkarusValueData const & value) - -> void; + static auto to_json(IkarusValueData const & value) -> nlohmann::json; IkarusValueDataVariant variant; }; diff --git a/src/ikarus/values/errors.hpp b/src/ikarus/values/errors.hpp index ed752af..f472d83 100644 --- a/src/ikarus/values/errors.hpp +++ b/src/ikarus/values/errors.hpp @@ -2,6 +2,8 @@ #include +#include + struct IkarusJsonMissingKeyError {}; struct IkarusJsonInvalidTypeError {}; @@ -34,3 +36,82 @@ using IkarusValueParseError = std::variant< using IkarusValuesParseError = std::variant; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusJsonMissingKeyError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "missing JSON key"); + } +}; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusJsonInvalidTypeError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "invalid JSON type"); + } +}; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusJsonEnumOutOfBoundsError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "JSON enum is out of bounds"); + } +}; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusJsonUnknownError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "unknown JSON error"); + } +}; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusValueSchemaParseError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to( + ctx.out(), + "failed to parse value schema: {}", + error.error + ); + } +}; + +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusValueDataParseError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to( + ctx.out(), + "failed to parse value data: {}", + error.error + ); + } +}; + +template<> +struct fmt::formatter : + formatter { + constexpr static auto format( + [[maybe_unused]] IkarusValueParseErrorDataSchemaMismatch const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "value data and schema mismatched"); + } +}; diff --git a/src/ikarus/values/schema.cpp b/src/ikarus/values/schema.cpp index e6cbd24..ea2e6dc 100644 --- a/src/ikarus/values/schema.cpp +++ b/src/ikarus/values/schema.cpp @@ -85,10 +85,10 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) return cppbase::ok(std::move(schema)); } -auto IkarusValueSchema::to_json( - nlohmann::json & json, - IkarusValueSchema const & schema -) -> void { +auto IkarusValueSchema::to_json(IkarusValueSchema const & schema) + -> nlohmann::json { + nlohmann::json json = nlohmann::json::object(); + std::visit( cppbase::overloaded{ [&json](IkarusValueSchemaPrimitive const & schema) { @@ -97,18 +97,14 @@ auto IkarusValueSchema::to_json( }, [&json](IkarusValueSchemaList const & schema) { json["type"] = IkarusValueSchemaType_List; - IkarusValueSchema::to_json(json["schema"], *schema.sub_schema); + json["schema"] = IkarusValueSchema::to_json(*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["key_schema"] = + IkarusValueSchema::to_json(*schema.key_schema); + json["value_schema"] = + IkarusValueSchema::to_json(*schema.value_schema); }, [&json](IkarusValueSchemaTuple const & schema) { json["type"] = IkarusValueSchemaType_Tuple; @@ -117,9 +113,9 @@ auto IkarusValueSchema::to_json( 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); + sub_schemas.push_back( + IkarusValueSchema::to_json(*sub_schema) + ); } json["schemas"] = sub_schemas; @@ -127,6 +123,8 @@ auto IkarusValueSchema::to_json( }, schema.variant ); + + return json; } auto IkarusValueSchema::validate(IkarusValueData const & data) const -> bool { diff --git a/src/ikarus/values/schema.hpp b/src/ikarus/values/schema.hpp index 4f4256c..7fd07ab 100644 --- a/src/ikarus/values/schema.hpp +++ b/src/ikarus/values/schema.hpp @@ -42,8 +42,7 @@ struct IkarusValueSchema { static auto from_json(nlohmann::json const & json) -> cppbase::Result; - static auto to_json(nlohmann::json & json, IkarusValueSchema const & value) - -> void; + static auto to_json(IkarusValueSchema const & value) -> nlohmann::json; auto validate(IkarusValueData const & data) const -> bool; diff --git a/src/ikarus/values/value.cpp b/src/ikarus/values/value.cpp index 07c0c24..9a23a98 100644 --- a/src/ikarus/values/value.cpp +++ b/src/ikarus/values/value.cpp @@ -25,34 +25,11 @@ auto IkarusValue::from_json(nlohmann::json const & json) return cppbase::ok(std::move(value)); } -auto IkarusValue::to_json(nlohmann::json & json, IkarusValue const & value) - -> void { - IkarusValueSchema::to_json(json["schema"], value.schema); - IkarusValueData::to_json(json["data"], value.data); -} - -auto IkarusValues::from_json(nlohmann::json const & json) - -> cppbase::Result { - IkarusValues values{}; - - for (auto const & json_entry : json) { - VTRY(auto name_json, get_key(json_entry, "name")); - - if (!name_json->is_string()) { - return cppbase::err(IkarusJsonInvalidTypeError{}); - } - - VTRY(auto value_json, get_key(json_entry, "value")); - - VTRY(auto value, IkarusValue::from_json(*value_json)); - - values.values.emplace_back( - std::make_pair( - std::move(name_json->get()), - std::move(value) - ) - ); - } - - return cppbase::ok(std::move(values)); +auto IkarusValue::to_json(IkarusValue const & value) -> nlohmann::json { + nlohmann::json json = nlohmann::json::object(); + + json["schema"] = IkarusValueSchema::to_json(value.schema); + json["data"] = IkarusValueData::to_json(value.data); + + return json; } diff --git a/src/ikarus/values/value.hpp b/src/ikarus/values/value.hpp index 47b3600..74db914 100644 --- a/src/ikarus/values/value.hpp +++ b/src/ikarus/values/value.hpp @@ -14,15 +14,5 @@ struct IkarusValue { static auto from_json(nlohmann::json const & json) -> cppbase::Result; - static auto to_json(nlohmann::json & json, IkarusValue const & value) - -> void; -}; - -struct IkarusValues { - static auto from_json(nlohmann::json const & json) - -> cppbase::Result; - static auto to_json(nlohmann::json & json, IkarusValues const & values) - -> void; - - std::vector> values; + static auto to_json(IkarusValue const & value) -> nlohmann::json; }; From bfac86b8a1f94ac2b04c1e754d7fed6370d8b6ad Mon Sep 17 00:00:00 2001 From: Folling Date: Thu, 2 Jan 2025 09:39:58 +0100 Subject: [PATCH 155/166] update dependencies Signed-off-by: Folling --- .clang-format | 3 +- include/ikarus/errors.h | 13 +- include/ikarus/objects/blueprint.h | 38 +- include/ikarus/objects/entity.h | 80 ++- include/ikarus/objects/property.h | 29 +- src/ikarus/errors.cpp | 4 +- src/ikarus/errors.hpp | 200 +++---- src/ikarus/objects/blueprint.cpp | 64 +++ src/ikarus/objects/entity.cpp | 508 ++++++++++++------ .../migrations/m0_initial_layout.sql | 3 +- src/ikarus/persistence/project.cpp | 43 ++ src/ikarus/values/data.cpp | 53 +- src/ikarus/values/data.hpp | 8 +- src/ikarus/values/errors.hpp | 13 + src/ikarus/values/schema.cpp | 80 ++- src/ikarus/values/schema.hpp | 4 + src/ikarus/values/shared.hpp | 4 +- src/ikarus/values/value.cpp | 17 +- src/ikarus/values/value.hpp | 2 + vendor/cppbase | 2 +- vendor/sqlitecpp | 2 +- 21 files changed, 800 insertions(+), 370 deletions(-) diff --git a/.clang-format b/.clang-format index 5456c6f..f88191b 100644 --- a/.clang-format +++ b/.clang-format @@ -66,7 +66,7 @@ BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon BreakStringLiterals: false -ColumnLimit: 80 +ColumnLimit: 120 CommentPragmas: '^\\.+' CompactNamespaces: false ConstructorInitializerIndentWidth: 4 @@ -148,6 +148,7 @@ PPIndentWidth: -1 PackConstructorInitializers: Never PointerAlignment: Middle + QualifierAlignment: Right # QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ] ReferenceAlignment: Middle diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index a41c291..eb123b0 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -31,24 +31,27 @@ enum IkarusErrorInfo { /// \brief The client provided a non-existent resource. /// Example: Passing an entity to a function after it has been deleted. IkarusErrorInfo_Client_NonExistent = 0x01000003, + /// \brief The client provided a resource which exists but is not linked to the current context. + /// Example: Passing a property that isn't linked to the current entity. + IkarusErrorInfo_Client_NotLinked = 0x01000004, /// \brief The client provided an index that was out of bounds for some array. /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3. - IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000004, + IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000005, /// \brief The client provided a numeric value that was out of bounds /// Example: Passing the value 2^32 to an i32 (might be passed as a string). - IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000005, + IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000006, /// \brief The client provided invalid input that doesn't fit in any of the other categories. /// Example: Passing an empty/blank string for a string that must be /// non-empty/-blank. - IkarusErrorInfo_Client_InvalidInput = 0x01000006, + IkarusErrorInfo_Client_InvalidInput = 0x01000007, /// \brief The client provided valid data in an invalid format. /// Example: Passing a malformed JSON string. - IkarusErrorInfo_Client_InvalidFormat = 0x01000007, + IkarusErrorInfo_Client_InvalidFormat = 0x01000008, /// \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, + IkarusErrorInfo_Client_ConstraintViolated = 0x10000009, // 0x02 reserved for dependency errors diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index adc97e9..7df09a8 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -20,6 +20,13 @@ IKARUS_BEGIN_HEADER /// in all linked entities. struct IkarusBlueprint; +/// \brief Checks whether a blueprint exists. +/// \param blueprint The blueprint to check. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return True if the blueprint exists, false otherwise or if an error occurs. +IKA_API bool ikarus_blueprint_exists(IkarusBlueprint * blueprint, IkarusErrorData * error_out); + /// \brief Flags for creating a blueprint. enum IkarusBlueprintCreateFlags { /// \brief No flags. @@ -48,14 +55,10 @@ IKA_API IkarusBlueprint * ikarus_blueprint_create( 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. +/// \details Each value of the entity will be copied into the blueprint as a blueprint. /// \param entity The entity to create the blueprint from. /// \pre \li Must not be null. /// \pre \li Must exist. @@ -117,10 +120,8 @@ IKA_API void ikarus_blueprint_delete( /// \param error_out \see errors.h /// \return The project the blueprint belongs to. /// \remark Ownership remains with libikarus. -IKA_API struct IkarusProject * ikarus_blueprint_get_project( - struct IkarusBlueprint * blueprint, - struct IkarusErrorData * error_out -); +IKA_API struct IkarusProject * +ikarus_blueprint_get_project(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); /// \brief Gets the name of a blueprint. /// \param blueprint The blueprint to get the name of. @@ -129,10 +130,7 @@ IKA_API struct IkarusProject * ikarus_blueprint_get_project( /// \param error_out \see errors.h /// \return The name of the blueprint. /// \remark Ownership remains with libikarus. -IKA_API char const * ikarus_blueprint_get_name( - struct IkarusBlueprint * blueprint, - struct IkarusErrorData * error_out -); +IKA_API char const * ikarus_blueprint_get_name(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); /// \brief Flags for setting the name of a blueprint. enum IkarusBlueprintSetNameFlags { @@ -163,7 +161,7 @@ IKA_API void ikarus_blueprint_set_name( /// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs. /// \param error_out \see errors.h /// \return The properties of the blueprint or null if an error occurs. -IKA_API struct IkarusProperty ** ikarus_blueprint_get_properties( +IKA_API struct IkarusBlueprint ** ikarus_blueprint_get_properties( struct IkarusBlueprint * blueprint, size_t * size_out, struct IkarusErrorData * error_out @@ -175,10 +173,8 @@ IKA_API struct IkarusProperty ** ikarus_blueprint_get_properties( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of properties of the blueprint or 0 if an error occurs. -IKA_API size_t ikarus_blueprint_get_properties_count( - struct IkarusBlueprint * blueprint, - struct IkarusErrorData * error_out -); +IKA_API size_t +ikarus_blueprint_get_properties_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); /// \brief Gets all entities linked to a blueprint. /// \param blueprint The blueprint to get the entities of. @@ -200,10 +196,8 @@ IKA_API struct IkarusEntity ** ikarus_blueprint_get_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of entities linked to the blueprint or 0 if an error occurs. -IKA_API size_t ikarus_blueprint_get_entities_count( - struct IkarusBlueprint * blueprint, - struct IkarusErrorData * error_out -); +IKA_API size_t +ikarus_blueprint_get_entities_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 172bad1..990be61 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -33,6 +33,14 @@ enum IkarusEntityCreateFlags { IkarusEntityCreateFlags_None = 0, }; +/// \brief Checks whether an entity exists. +/// \param entity The entity to check. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return True if the entity exists, false otherwise or if an error occurs. +IKA_API bool +ikarus_entity_exists(IkarusEntity * entity, IkarusErrorData * error_out); + /// \brief Creates a new entity. /// \param project The project to create the entity in. /// \pre \li Must not be null. @@ -127,6 +135,22 @@ IKA_API void ikarus_entity_set_name( IkarusErrorData * error_out ); +/// \brief Gets whether an entity is linked to a blueprint. +/// \param entity The entity to check the blueprint of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param blueprint The blueprint to check the entity's link to. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \pre \li Must be in the same project as the entity. +/// \param error_out \see errors.h +/// \return True if the entity is linked to the blueprint, false otherwise or if an error occurs. +IKA_API bool ikarus_entity_is_linked_to_blueprint( + IkarusEntity * entity, + struct IkarusBlueprint * blueprint, + IkarusErrorData * error_out +); + /// \brief Gets the blueprints an entity is linked to. /// \param entity The entity to get the blueprints of. /// \pre \li Must not be null. @@ -191,6 +215,7 @@ enum IkarusEntityUnlinkBlueprintFlags { /// \param blueprint The blueprint to unlink from. /// \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 not linked to the blueprint, nothing happens. /// \param flags Flags for unlinking the entity from the blueprint. /// \param error_out \see errors.h @@ -201,6 +226,20 @@ IKA_API void ikarus_entity_unlink_blueprint( IkarusErrorData * error_out ); +/// \brief Gets whether an entity has a value. +/// \param entity The entity to check the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param name The value's name. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return True if the entity has a value with the name, false otherwise or if an error occurs. +IKA_API bool ikarus_entity_has_value( + IkarusEntity * entity, + char const * name, + IkarusErrorData * error_out +); + /// \brief Struct for an entity value. struct IkarusEntityValue { /// \brief The name of the value. @@ -229,7 +268,7 @@ IKA_API IkarusEntityValue * ikarus_entity_get_values( /// \pre \li Must exist. /// \param name The value's name. /// \pre \li Must not be null. -/// \remark Ownership remains with the client. +/// \pre \li Must exist. /// \param error_out \see errors.h /// \return The value, in json format of or null if an error occurs. \see value.h IKA_API char const * ikarus_entity_get_value( @@ -287,6 +326,22 @@ IKA_API void ikarus_entity_delete_value( IkarusErrorData * error_out ); +/// \brief Gets whether an entity has a property value. +/// \param entity The entity to check the property value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to check the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \pre \li Must be in the same project as the entity. +/// \param error_out \see errors.h +/// \return True if the entity has a value for the property, false otherwise or if an error occurs. +IKA_API bool ikarus_entity_has_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + IkarusErrorData * error_out +); + /// \brief Struct for an entity property value. struct IkarusEntityPropertyValue { /// \brief The property. @@ -355,6 +410,29 @@ IKA_API void ikarus_entity_set_property_value( IkarusErrorData * error_out ); +/// \brief Flags for clearing the value of a property of an entity. +enum IkarusEntityClearPropertyValueFlags { + /// \brief No flags. + IkarusEntityClearPropertyValueFlags_None = 0, +}; + +/// \brief Clears the value of a property of an entity. +/// \param entity The entity to clear the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to clear the value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \pre \li Must be linked to the entity. +/// \param flags Flags for clearing the property value. +/// \param error_out \see errors.h +IKA_API void ikarus_entity_clear_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + IkarusEntitySetPropertyValueFlags flags, + IkarusErrorData * error_out +); + IKARUS_END_HEADER /// @} diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index 5bff42c..2124423 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -36,6 +36,13 @@ IKARUS_BEGIN_HEADER /// property's default value if none is specified. struct IkarusProperty; +/// \brief Checks whether a property exists. +/// \param property The property to check. +/// \pre \li Must not be null. +/// \param error_out \see errors.h +/// \return True if the property exists, false otherwise or if an error occurs. +IKA_API bool ikarus_property_exists(IkarusProperty * property, IkarusErrorData * error_out); + /// \brief Flags for creating a property. enum IkarusPropertyCreateFlags { /// \brief No flags. @@ -73,11 +80,8 @@ enum IkarusPropertyDeleteFlags { /// \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 -); +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. @@ -86,10 +90,7 @@ IKA_API void ikarus_property_delete( /// \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 -); +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. @@ -98,10 +99,7 @@ IKA_API struct IkarusProject * ikarus_property_get_project( /// \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 -); +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. @@ -110,10 +108,7 @@ IKA_API char const * ikarus_property_get_name( /// \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 -); +IKA_API struct IkarusValueSchema * ikarus_property_get_schema(IkarusProperty * property, IkarusErrorData * error_out); /// \brief Flags for setting the name of a property. enum IkarusPropertySetNameFlags { diff --git a/src/ikarus/errors.cpp b/src/ikarus/errors.cpp index b3e4ae2..07f7a15 100644 --- a/src/ikarus/errors.cpp +++ b/src/ikarus/errors.cpp @@ -64,9 +64,9 @@ char const * ikarus_get_error_info_name(IkarusErrorInfo info) { } bool ikarus_error_data_is_success(IkarusErrorData const * data) { - return data->info == IkarusErrorInfo_None; + return data && data->info == IkarusErrorInfo_None; } bool ikarus_error_data_is_error(IkarusErrorData const * data) { - return data->info != IkarusErrorInfo_None; + return data && data->info != IkarusErrorInfo_None; } diff --git a/src/ikarus/errors.hpp b/src/ikarus/errors.hpp index a9daa9d..d4294ec 100644 --- a/src/ikarus/errors.hpp +++ b/src/ikarus/errors.hpp @@ -3,24 +3,33 @@ #include #include +#include +#include +#include + #include -void safe_strcpy( - std::string_view const src, - char * dest, - size_t const dest_size -); +void safe_strcpy(std::string_view const src, char * dest, size_t const dest_size); + +template<> +struct fmt::formatter : formatter { + constexpr static auto format([[maybe_unused]] IkarusErrorData const & error, fmt::format_context & ctx) { + return fmt::format_to( + ctx.out(), + "ERROR {}({}): {}", + ikarus_error_info_get_name(error.info), + static_cast>(error.info), + error.message + ); + } +}; #define IKARUS_VOID_RETURN -#define IKARUS_SET_ERROR(msg, err_info) \ - if (error_out != nullptr) { \ - safe_strcpy( \ - msg, \ - static_cast(error_out->message), \ - IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT \ - ); \ - error_out->info = err_info; \ +#define IKARUS_SET_ERROR(msg, err_info) \ + if (error_out != nullptr) { \ + safe_strcpy(msg, static_cast(error_out->message), IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT); \ + error_out->info = err_info; \ } #define IKARUS_FAIL(ret, msg, err_info) \ @@ -33,142 +42,75 @@ void safe_strcpy( return ret; \ } -#define IKARUS_TRY_OR_FAIL_IMPL(var_name, msg, err_info, ...) \ - auto var_name = __VA_ARGS__; \ - 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_TRY_OR_FAIL_IMPL(var_name, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + 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_TRY_OR_FAIL(msg, err_info, ...) \ - IKARUS_TRY_OR_FAIL_IMPL( \ - CPPBASE_UNIQUE_NAME(result), \ - msg, \ - err_info, \ - __VA_ARGS__ \ - ); + IKARUS_TRY_OR_FAIL_IMPL(CPPBASE_UNIQUE_NAME(result), msg, err_info, __VA_ARGS__); -#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR( \ - fmt::format( \ - fmt::runtime(msg), \ - std::move(var_name).unwrap_error() \ - ), \ - err_info \ - ); \ - return ret; \ +#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(fmt::runtime(msg), std::move(var_name).unwrap_error()), err_info); \ + return ret; \ } #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, ...) \ - auto var_name = __VA_ARGS__; \ - 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_VTRY_OR_FAIL_IMPL(var_name, value, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(fmt::runtime(msg), 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, ...) \ - 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, ...) \ - auto var_name = __VA_ARGS__; \ - if (var_name.is_error()) { \ - IKARUS_SET_ERROR( \ - fmt::format(fmt::runtime(msg), var_name.unwrap_error()), \ - err_info \ - ); \ - return ret; \ - } \ +#define IKARUS_VTRYRV_OR_FAIL_IMPL(var_name, value, ret, msg, err_info, ...) \ + auto var_name = __VA_ARGS__; \ + if (var_name.is_error()) { \ + IKARUS_SET_ERROR(fmt::format(fmt::runtime(msg), var_name.unwrap_error()), err_info); \ + return ret; \ + } \ value = std::move(var_name).unwrap_value() #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_ERROR(ret, error) \ + if (ikarus_error_data_is_error(error)) { \ + if (error_out) { \ + *error_out = *error; \ + } \ + 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_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 \ - ); + IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput) -#define IKARUS_FAIL_IF_NOT_EXIST_IMPL(exists_name, object, ret) \ - IKARUS_VTRYRV_OR_FAIL( \ - auto exists_name, \ - ret, \ - fmt::format( \ - "failed to check if {} exists", \ - std::remove_cvref_t::object_name \ - ), \ - IkarusErrorInfo_Database_QueryFailed, \ - object->project->db->query_one( \ - fmt::format( \ - "SELECT EXISTS(SELECT 1 FROM `{}` WHERE `id` = ?)", \ - std::remove_cvref_t::table_name \ - ), \ - object->id \ - ) \ - ); \ - \ - IKARUS_FAIL_IF( \ - !exists_name, \ - ret, \ - fmt::format( \ - "{} doesn't exist", \ - std::remove_cvref_t::object_name \ - ), \ - IkarusErrorInfo_Client_NonExistent \ - ) +#define IKARUS_ASCERTAIN_IMPL(cond_name, ret, msg, err_info, func, ...) \ + auto cond_name = std::invoke(func, __VA_ARGS__, error_out); \ + IKARUS_FAIL_IF_ERROR(ret, error_out) \ + IKARUS_FAIL_IF(!cond_name, ret, msg, err_info) -#define IKARUS_FAIL_IF_NOT_EXIST(object, ret) \ - IKARUS_FAIL_IF_NOT_EXIST_IMPL(CPPBASE_UNIQUE_NAME(exists), object, ret) +#define IKARUS_ASCERTAIN(ret, msg, err_info, func, ...) \ + IKARUS_ASCERTAIN_IMPL(CPPBASE_UNIQUE_NAME(cond), ret, msg, err_info, func, __VA_ARGS__); + +#define IKARUS_CALL(ret, func, ...) \ + std::invoke(func, __VA_ARGS__, error_out); \ + IKARUS_FAIL_IF_ERROR(ret, error_out); + +#define IKARUS_VCALL(value, ret, func, ...) \ + value = std::invoke(func, __VA_ARGS__, error_out); \ + IKARUS_FAIL_IF_ERROR(ret, error_out); diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index 553bbe2..ba9e7b1 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -2,8 +2,72 @@ #include #include +#include #include IkarusBlueprint::IkarusBlueprint(struct IkarusProject * project, int64_t id): project{project}, id{id} {} + +IkarusBlueprint * ikarus_blueprint_create( + struct IkarusProject * project, + char const * name, + IkarusBlueprintCreateFlags flags, + struct IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to create entity: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->execute("INSERT INTO `blueprints`(`name`) VALUES(?)", name) + ); + + auto const id = project->db->last_insert_rowid(); + return new IkarusBlueprint{project, id}; +} + +IkarusBlueprint * ikarus_blueprint_create_from_entity( + struct IkarusEntity * entity, + char const * name, + IkarusBlueprintCreateFromEntityFlags flags, + struct IkarusErrorData * error_out +) { + IKARUS_TRYRV_OR_FAIL( + nullptr, + "{}", + IkarusErrorInfo_Database_QueryFailed, + ikarus_libikarus_func_call_to_result( + error_out, + ikarus_must_return_true(ikarus_entity_exists, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent), + entity + ) + ); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto id, + nullptr, + "failed to create blueprint from entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->transact([&](auto * db) -> cppbase::Result { + CPPBASE_TRY(db->execute("INSERT INTO `blueprints`(`name`) VALUES(?)", name)); + + auto const id = db->last_insert_rowid(); + + CPPBASE_TRY(entity->project->db->execute( + "INSERT INTO `properties`(`blueprint`, `name`, `schema`) " + "SELECT ?, `name`, json_extract(`value`, '$.schema') FROM `entity_values` " + "WHERE `entity` = ?", + id, + entity->id + )) + + return cppbase::ok(entity->project->db->last_insert_rowid()); + }) + ); + + return new IkarusBlueprint{entity->project, id}; +} diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 5abd085..aae7564 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -4,13 +4,28 @@ #include #include +#include #include +#include #include IkarusEntity::IkarusEntity(struct IkarusProject * project, int64_t id): project{project}, id{id} {} +bool ikarus_entity_exists(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(entity, false); + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check whether entity exists: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one("SELECT EXISTS(SELECT 1 FROM `entities` WHERE `id` = ?)", entity->id) + ); + + return exists; +} + IkarusEntity * ikarus_entity_create( struct IkarusProject * project, char const * name, @@ -31,98 +46,83 @@ IkarusEntity * ikarus_entity_create( return new IkarusEntity{project, id}; } -void ikarus_entity_delete( - IkarusEntity * entity, - IkarusEntityDeleteFlags flags, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); +void ikarus_entity_delete(IkarusEntity * entity, IkarusEntityDeleteFlags flags, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); IKARUS_TRYRV_OR_FAIL( IKARUS_VOID_RETURN, "failed to delete entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db - ->execute("DELETE FROM `entities` WHERE `id` = ?", entity->id) + entity->project->db->execute("DELETE FROM `entities` WHERE `id` = ?", entity->id) ); delete entity; } -IkarusEntity * ikarus_entity_copy( - IkarusEntity * entity, - IkarusEntityCopyFlags flags, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); +IkarusEntity * ikarus_entity_copy(IkarusEntity * entity, IkarusEntityCopyFlags flags, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto id, nullptr, "failed to copy entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->transact( - [entity](auto * db) - -> cppbase::Result { - TRY(entity->project->db->execute( - "INSERT INTO `entities`(`name`) " - "SELECT `name` FROM `entities` WHERE `id` = ?", - entity->id - )); + entity->project->db->transact([entity](auto * db) -> cppbase::Result { + CPPBASE_TRY(entity->project->db->execute( + "INSERT INTO `entities`(`name`) " + "SELECT `name` FROM `entities` WHERE `id` = ?", + entity->id + )); - TRY(entity->project->db->execute( - "INSERT INTO `entity_values`(`entity`, `name`, `value`) " - "SELECT ?1, `name`, `value` FROM `entity_values` WHERE " - "`entity` = ?1", - entity->id - )) + CPPBASE_TRY(entity->project->db->execute( + "INSERT INTO `entity_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 - )) + CPPBASE_TRY(entity->project->db->execute( + "INSERT INTO `entity_property_values`(`entity`, `property`, `value`) " + "SELECT ?1, `property`, `value` FROM `entity_property_values` " + "WHERE `entity` = ?1", + entity->id + )) - 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 - )) + CPPBASE_TRY(entity->project->db->execute( + "INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) " + "SELECT ?1, `property`, `value` FROM `entity_property_values` " + "WHERE `entity` = ?1", + entity->id + )) - return cppbase::ok(entity->project->db->last_insert_rowid()); - } - ) + return cppbase::ok(entity->project->db->last_insert_rowid()); + }) ); return new IkarusEntity{entity->project, id}; } -IkarusProject * -ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); +IkarusProject * ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); return entity->project; } -char const * -ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); +char const * ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto name, nullptr, "failed to get name for entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->query_one( - "SELECT `name` FROM `entities` WHERE `id` = ?", - entity->id - ) + entity->project->db->query_one("SELECT `name` FROM `entities` WHERE `id` = ?", entity->id) ); return name; @@ -134,33 +134,63 @@ void ikarus_entity_set_name( IkarusEntitySetNameFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN); IKARUS_TRYRV_OR_FAIL( IKARUS_VOID_RETURN, "failed to set name for entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->execute( - "UPDATE `entities` SET `name` = ? WHERE `id` = ?", - name, - entity->id - ) + entity->project->db->execute("UPDATE `entities` SET `name` = ? WHERE `id` = ?", name, entity->id) ); } -struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( +bool ikarus_entity_is_linked_to_blueprint( IkarusEntity * entity, - size_t * size_out, + struct IkarusBlueprint * blueprint, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); - IKARUS_FAIL_IF_NULL(size_out, nullptr); + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_ASCERTAIN( + false, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); - auto count = ikarus_entity_get_linked_blueprints_count(entity, error_out); - IKARUS_FAIL_IF_ERROR(nullptr); + IKARUS_FAIL_IF( + entity->project != blueprint->project, + false, + "blueprint does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); + + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check if entity is linked to blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT EXISTS(SELECT 1 FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?)", + entity->id, + blueprint->id + ) + ); + + return exists; +} + +struct IkarusBlueprint ** +ikarus_entity_get_linked_blueprints(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_VCALL(auto count, false, ikarus_entity_get_linked_blueprints_count, entity); std::int64_t ids[count]; @@ -189,22 +219,16 @@ struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( return blueprints; } -size_t ikarus_entity_get_linked_blueprints_count( - IkarusEntity * entity, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, 0); - IKARUS_FAIL_IF_NOT_EXIST(entity, 0); +size_t ikarus_entity_get_linked_blueprints_count(IkarusEntity * entity, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto count, 0, "failed to get linked blueprints count for entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->query_one( - "SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", - entity->id - ) + entity->project->db + ->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", entity->id) ); return count; @@ -216,10 +240,28 @@ void ikarus_entity_link_blueprint( IkarusEntityLinkBlueprintFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NULL(blueprint, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(blueprint, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_FAIL_IF( + entity->project != blueprint->project, + IKARUS_VOID_RETURN, + "blueprint does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); IKARUS_TRYRV_OR_FAIL( IKARUS_VOID_RETURN, @@ -239,10 +281,28 @@ void ikarus_entity_unlink_blueprint( IkarusEntityUnlinkBlueprintFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NULL(blueprint, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(blueprint, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_FAIL_IF( + entity->project != blueprint->project, + IKARUS_VOID_RETURN, + "blueprint does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); IKARUS_TRYRV_OR_FAIL( IKARUS_VOID_RETURN, @@ -256,13 +316,28 @@ void ikarus_entity_unlink_blueprint( ); } -IkarusEntityValue * ikarus_entity_get_values( - IkarusEntity * entity, - size_t * size_out, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); +bool ikarus_entity_has_value(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(name, false); + + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check if entity has value: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT EXISTS(SELECT 1 FROM `entity_values` WHERE `entity` = ? AND `name` = ?)", + entity->id, + name + ) + ); + + return exists; +} + +IkarusEntityValue * ikarus_entity_get_values(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto values_plain, @@ -277,17 +352,9 @@ IkarusEntityValue * ikarus_entity_get_values( IkarusEntityValue * values = new IkarusEntityValue[values_plain.size()]; - std::transform( - std::cbegin(values_plain), - std::cend(values_plain), - values, - [](auto const & tuple) { - return IkarusEntityValue{ - tuple.template get<0>(), - tuple.template get<1>() - }; - } - ); + std::transform(std::cbegin(values_plain), std::cend(values_plain), values, [](auto const & tuple) { + return IkarusEntityValue{tuple.template get<0>(), tuple.template get<1>()}; + }); if (size_out) { *size_out = values_plain.size(); @@ -296,15 +363,19 @@ IkarusEntityValue * ikarus_entity_get_values( return values; } -char const * ikarus_entity_get_value( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); +char const * ikarus_entity_get_value(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_ASCERTAIN( + nullptr, + "entity doesn't have value", + IkarusErrorInfo_Client_NotLinked, + ikarus_entity_has_value, + entity, + name + ); + IKARUS_VTRYRV_OR_FAIL( auto value, nullptr, @@ -326,10 +397,16 @@ void ikarus_entity_set_value( char const * value, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN); IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); // parsing from & to here to ensure values are valid JSON & formatted // uniformly @@ -338,7 +415,7 @@ void ikarus_entity_set_value( IKARUS_VOID_RETURN, "cannot parse value as JSON: {}", IkarusErrorInfo_Client_InvalidInput, - IkarusValue::from_json(value) + IkarusValue::from_json_str(value) ); IKARUS_TRYRV_OR_FAIL( @@ -361,29 +438,64 @@ void ikarus_entity_delete_value( IkarusEntityDeleteValueFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); + IKARUS_TRYRV_OR_FAIL( IKARUS_VOID_RETURN, "failed to delete value for entity: {}", IkarusErrorInfo_Database_QueryFailed, - entity->project->db->execute( - "DELETE FROM `entity_values` WHERE `entity` = ? AND `name` = ?", - entity->id, - name - ) + entity->project->db->execute("DELETE FROM `entity_values` WHERE `entity` = ? AND `name` = ?", entity->id, name) ); } -IkarusEntityPropertyValue * ikarus_entity_get_property_values( +bool ikarus_entity_has_property_value( IkarusEntity * entity, - size_t * size_out, + struct IkarusProperty * property, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + + IKARUS_ASCERTAIN( + false, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_FAIL_IF( + entity->project != property->project, + false, + "property does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); + + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check if entity has property value: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->query_one( + "SELECT EXISTS(SELECT 1 FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?)", + entity->id, + property->id + ) + ); + + return exists; +} + +IkarusEntityPropertyValue * +ikarus_entity_get_property_values(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto values_plain, @@ -391,24 +503,20 @@ IkarusEntityPropertyValue * ikarus_entity_get_property_values( "failed to get property values for entity: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_many( - "SELECT `property`, `value` FROM `entity_property_values` WHERE `entity` = ?", + "SELECT `e`.`property`, IFNULL(`e`.`value`, ikarus_default_value(`p`.`schema`)) FROM `entity_property_values` AS `e` " + "INNER JOIN `properties` AS `p` ON `p`.`id` = `e`.`property` " + " WHERE `e`.`entity` = ?", entity->id ) ); - IkarusEntityPropertyValue * values = - new IkarusEntityPropertyValue[values_plain.size()]; - std::transform( - std::cbegin(values_plain), - std::cend(values_plain), - values, - [entity](auto const & tuple) { - return IkarusEntityPropertyValue{ - new IkarusProperty{entity->project, tuple.template get<0>()}, - tuple.template get<1>() - }; - } - ); + IkarusEntityPropertyValue * values = new IkarusEntityPropertyValue[values_plain.size()]; + std::transform(std::cbegin(values_plain), std::cend(values_plain), values, [entity](auto const & tuple) { + return IkarusEntityPropertyValue{ + new IkarusProperty{entity->project, tuple.template get<0>()}, + tuple.template get<1>() + }; + }); if (size_out) { *size_out = values_plain.size(); @@ -417,15 +525,23 @@ IkarusEntityPropertyValue * ikarus_entity_get_property_values( return values; } -char const * ikarus_entity_get_property_value( - IkarusEntity * entity, - struct IkarusProperty * property, - IkarusErrorData * error_out -) { - IKARUS_FAIL_IF_NULL(entity, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); - IKARUS_FAIL_IF_NULL(property, nullptr); - IKARUS_FAIL_IF_NOT_EXIST(property, nullptr); +char const * +ikarus_entity_get_property_value(IkarusEntity * entity, struct IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_ASCERTAIN( + false, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_FAIL_IF( + entity->project != property->project, + nullptr, + "property does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); IKARUS_VTRYRV_OR_FAIL( auto value, @@ -433,7 +549,9 @@ char const * ikarus_entity_get_property_value( "failed to get property value for entity: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_one( - "SELECT `value` FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?", + "SELECT IFNULL(`e`.`value`, ikarus_default_value(`p`.`schema`)) FROM `entity_property_values` AS `e` " + "INNER JOIN `properties` AS `p` ON `p`.`id` = `e`.`property` " + "WHERE `e`.`entity` = ? AND `e`.`property` = ?", entity->id, property->id ) @@ -449,12 +567,38 @@ void ikarus_entity_set_property_value( IkarusEntitySetPropertyValueFlags flags, IkarusErrorData * error_out ) { - IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NULL(property, IKARUS_VOID_RETURN); - IKARUS_FAIL_IF_NOT_EXIST(property, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF( + entity->project != property->project, + IKARUS_VOID_RETURN, + "property does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity does not have property value", + IkarusErrorInfo_Client_NotLinked, + ikarus_entity_has_property_value, + entity, + property + ); + // parsing from & to here to ensure values are valid JSON & formatted // uniformly IKARUS_VTRYRV_OR_FAIL( @@ -462,7 +606,7 @@ void ikarus_entity_set_property_value( IKARUS_VOID_RETURN, "cannot parse value as JSON: {}", IkarusErrorInfo_Client_InvalidInput, - IkarusValue::from_json(value) + IkarusValue::from_json_str(value) ); IKARUS_TRYRV_OR_FAIL( @@ -478,3 +622,51 @@ void ikarus_entity_set_property_value( ) ); } + +void ikarus_entity_clear_property_value( + IkarusEntity * entity, + struct IkarusProperty * property, + IkarusEntitySetPropertyValueFlags flags, + IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_entity_exists, + entity + ); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_FAIL_IF( + entity->project != property->project, + IKARUS_VOID_RETURN, + "property does not belong to entity's project", + IkarusErrorInfo_Client_NotLinked + ); + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "entity doesn't have property", + IkarusErrorInfo_Client_NotLinked, + ikarus_entity_has_property_value, + entity, + property + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to clear property value for entity: {}", + IkarusErrorInfo_Database_QueryFailed, + entity->project->db->execute( + "DELETE FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?", + entity->id, + property->id + ) + ); +} diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index e83b41a..50fb7f0 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -26,8 +26,7 @@ CREATE TABLE `properties` `blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE, `name` TEXT NOT NULL, `schema` TEXT NOT NULL, - `default_value` TEXT NOT NULL, - `settings` TEXT NOT NULL + `default_value` TEXT ) STRICT; CREATE TABLE `entity_blueprint_links` diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 75c8825..0028486 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -73,6 +73,49 @@ auto create_impl( .on_error(close_db) ); + db->create_function( + "ikarus_default_value", + 1, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + nullptr, + [](sqlite3_context * ctx, int argc, sqlite3_value ** argv) { + if (sqlite3_value_type(argv[0]) != SQLITE_TEXT) { + sqlite3_result_error( + ctx, + "expected the 'schema' parameter to be of type text", + SQLITE_MISMATCH + ); + return; + } + + auto const schema_json = + reinterpret_cast(sqlite3_value_text(argv[0])); + + auto schema_res = IkarusValueSchema::from_json_str(schema_json); + + if (schema_res.is_error()) { + sqlite3_result_error( + ctx, + "failed to parse schema", + SQLITE_ERROR + ); + return; + } + + auto schema = std::move(schema_res).unwrap_value(); + + auto default_value_json = + IkarusValue::to_json(schema.default_value()).dump(); + + sqlite3_result_text( + ctx, + default_value_json.data(), + default_value_json.size(), + SQLITE_TRANSIENT + ); + } + ); + return std::move(db); } diff --git a/src/ikarus/values/data.cpp b/src/ikarus/values/data.cpp index f7f6845..af9a1fc 100644 --- a/src/ikarus/values/data.cpp +++ b/src/ikarus/values/data.cpp @@ -20,7 +20,7 @@ auto get_primitive_type(IkarusValueDataPrimitive const & primitive) [](IkarusValueDataPrimitiveNumber const &) { return IkarusValuePrimitiveType_Number; }, - [](IkarusValueDataPrimitiveString const &) { + [](IkarusValueDataPrimitiveText const &) { return IkarusValuePrimitiveType_Text; } }, @@ -36,7 +36,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json) IkarusValueData value{}; - VTRY( + CPPBASE_VTRY( auto type, deserialize_enum( json, @@ -48,7 +48,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json) switch (type) { case IkarusValueDataType_Primitive: { - VTRY( + CPPBASE_VTRY( auto primitive, deserialize_enum( json, @@ -60,21 +60,21 @@ auto IkarusValueData::from_json(nlohmann::json const & json) switch (primitive) { case IkarusValuePrimitiveType_Toggle: { - VTRY(auto data, deserialize_any(json, "data")); + CPPBASE_VTRY(auto data, deserialize_any(json, "data")); value.variant = IkarusValueDataPrimitiveToggle{data}; break; } case IkarusValuePrimitiveType_Number: { - VTRY(auto data, deserialize_any(json, "data")); + CPPBASE_VTRY(auto data, deserialize_any(json, "data")); value.variant = IkarusValueDataPrimitiveNumber{data}; break; } case IkarusValuePrimitiveType_Text: { - VTRY(auto data, deserialize_any(json, "data")); + CPPBASE_VTRY(auto data, deserialize_any(json, "data")); - value.variant = IkarusValueDataPrimitiveString{data}; + value.variant = IkarusValueDataPrimitiveText{data}; break; } } @@ -83,14 +83,17 @@ auto IkarusValueData::from_json(nlohmann::json const & json) } case IkarusValueDataType_List: { std::vector> values_data{}; - VTRY( + CPPBASE_VTRY( auto data_json, deserialize_any>(json, "data") ); values_data.reserve(data_json.size()); for (auto const & data_json : data_json) { - VTRY(auto value_data, IkarusValueData::from_json(data_json)); + CPPBASE_VTRY( + auto value_data, + IkarusValueData::from_json(data_json) + ); values_data.emplace_back( cppbase::make_owning(std::move(value_data)) ); @@ -105,7 +108,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json) cppbase::owning_ptr>> map_data{}; - VTRY( + CPPBASE_VTRY( auto map_data_json, deserialize_any>(json, "data") ); @@ -113,10 +116,10 @@ auto IkarusValueData::from_json(nlohmann::json const & json) 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)); + CPPBASE_VTRY(auto key_json, get_key(pair_json, "key")); + CPPBASE_VTRY(auto value_json, get_key(pair_json, "value")); + CPPBASE_VTRY(auto key, IkarusValueData::from_json(*key_json)); + CPPBASE_VTRY(auto value, IkarusValueData::from_json(*value_json)); map_data.emplace_back( cppbase::make_owning(key), @@ -130,7 +133,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json) case IkarusValueDataType_Tuple: { std::vector> values_data{}; - VTRY( + CPPBASE_VTRY( auto values_json, deserialize_any>(json, "data") ); @@ -138,7 +141,10 @@ auto IkarusValueData::from_json(nlohmann::json const & json) values_data.reserve(values_json.size()); for (auto const & value_json : values_json) { - VTRY(auto value_data, IkarusValueData::from_json(value_json)); + CPPBASE_VTRY( + auto value_data, + IkarusValueData::from_json(value_json) + ); values_data.emplace_back( cppbase::make_owning(value_data) ); @@ -152,6 +158,19 @@ auto IkarusValueData::from_json(nlohmann::json const & json) return cppbase::ok(value); } +auto IkarusValueData::from_json_str(std::string_view json_str) + -> cppbase::Result { + auto json = nlohmann::json::parse(json_str, nullptr, false); + + if (json.is_discarded()) { + return cppbase::err( + IkarusValueDataParseError{IkarusJsonError{IkarusJsonParseError{}}} + ); + } + + return IkarusValueData::from_json(json); +} + auto IkarusValueData::to_json(IkarusValueData const & value) -> nlohmann::json { nlohmann::json json = nlohmann::json::object(); @@ -170,7 +189,7 @@ auto IkarusValueData::to_json(IkarusValueData const & value) -> nlohmann::json { json["primitive"] = IkarusValuePrimitiveType_Number; json["data"] = number.value; }, - [&](IkarusValueDataPrimitiveString const & string) { + [&](IkarusValueDataPrimitiveText const & string) { json["type"] = IkarusValueDataType_Primitive; json["primitive"] = IkarusValuePrimitiveType_Text; json["data"] = string.value; diff --git a/src/ikarus/values/data.hpp b/src/ikarus/values/data.hpp index 1c9b3d8..2e68949 100644 --- a/src/ikarus/values/data.hpp +++ b/src/ikarus/values/data.hpp @@ -22,14 +22,14 @@ struct IkarusValueDataPrimitiveNumber { double value; }; -struct IkarusValueDataPrimitiveString { +struct IkarusValueDataPrimitiveText { std::string value; }; using IkarusValueDataPrimitive = std::variant< IkarusValueDataPrimitiveToggle, IkarusValueDataPrimitiveNumber, - IkarusValueDataPrimitiveString>; + IkarusValueDataPrimitiveText>; struct IkarusValueDataList { std::vector> values; @@ -53,7 +53,9 @@ struct IkarusValueData { IkarusValueDataMap, IkarusValueDataTuple>; - static auto from_json(nlohmann::json const & json) + static auto from_json(nlohmann::json const & json_str) + -> cppbase::Result; + static auto from_json_str(std::string_view json_str) -> cppbase::Result; static auto to_json(IkarusValueData const & value) -> nlohmann::json; diff --git a/src/ikarus/values/errors.hpp b/src/ikarus/values/errors.hpp index f472d83..1b8a880 100644 --- a/src/ikarus/values/errors.hpp +++ b/src/ikarus/values/errors.hpp @@ -10,12 +10,15 @@ struct IkarusJsonInvalidTypeError {}; struct IkarusJsonEnumOutOfBoundsError {}; +struct IkarusJsonParseError {}; + struct IkarusJsonUnknownError {}; using IkarusJsonError = std::variant< IkarusJsonMissingKeyError, IkarusJsonInvalidTypeError, IkarusJsonEnumOutOfBoundsError, + IkarusJsonParseError, IkarusJsonUnknownError>; struct IkarusValueSchemaParseError { @@ -57,6 +60,16 @@ struct fmt::formatter : formatter { } }; +template<> +struct fmt::formatter : formatter { + constexpr static auto format( + [[maybe_unused]] IkarusJsonParseError const & error, + fmt::format_context & ctx + ) { + return fmt::format_to(ctx.out(), "buffer isn't valid JSON"); + } +}; + template<> struct fmt::formatter : formatter { constexpr static auto format( diff --git a/src/ikarus/values/schema.cpp b/src/ikarus/values/schema.cpp index ea2e6dc..3c53fe3 100644 --- a/src/ikarus/values/schema.cpp +++ b/src/ikarus/values/schema.cpp @@ -5,6 +5,7 @@ #include #include #include +#include auto IkarusValueSchema::from_json(nlohmann::json const & json) -> cppbase::Result { @@ -18,7 +19,7 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) IkarusValueSchema schema{}; - VTRY( + CPPBASE_VTRY( auto type, deserialize_enum( json, @@ -30,7 +31,7 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) switch (type) { case IkarusValueSchemaType_Primitive: { - VTRY( + CPPBASE_VTRY( auto primitive, deserialize_enum( json, @@ -43,15 +44,21 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) break; } case IkarusValueSchemaType_List: { - VTRY(auto sub_schema, IkarusValueSchema::from_json(json["schema"])); + CPPBASE_VTRY( + auto sub_schema, + IkarusValueSchema::from_json(json["schema"]) + ); schema.variant = IkarusValueSchemaList{ cppbase::make_owning(std::move(sub_schema)) }; break; } case IkarusValueSchemaType_Map: { - VTRY(auto key_schema, IkarusValueSchema::from_json(json["key_schema"])); - VTRY( + CPPBASE_VTRY( + auto key_schema, + IkarusValueSchema::from_json(json["key_schema"]) + ); + CPPBASE_VTRY( auto value_schema, IkarusValueSchema::from_json(json["value_schema"]) ); @@ -62,7 +69,7 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) break; } case IkarusValueSchemaType_Tuple: { - VTRY( + CPPBASE_VTRY( auto sub_schemas_json, deserialize_any>(json, "schemas") ); @@ -71,7 +78,10 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) 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)); + CPPBASE_VTRY( + auto schema, + IkarusValueSchema::from_json(sub_schema_json) + ); sub_schemas.emplace_back( cppbase::make_owning(std::move(schema)) ); @@ -85,6 +95,19 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json) return cppbase::ok(std::move(schema)); } +auto IkarusValueSchema::from_json_str(std::string_view json_str) + -> cppbase::Result { + auto json = nlohmann::json::parse(json_str, nullptr, false); + + if (json.is_discarded()) { + return cppbase::err( + IkarusValueSchemaParseError{IkarusJsonError{IkarusJsonParseError{}}} + ); + } + + return IkarusValueSchema::from_json(json); +} + auto IkarusValueSchema::to_json(IkarusValueSchema const & schema) -> nlohmann::json { nlohmann::json json = nlohmann::json::object(); @@ -173,3 +196,46 @@ auto IkarusValueSchema::validate(IkarusValueData const & data) const -> bool { data.variant ); } + +auto IkarusValueSchema::default_value_data() const -> IkarusValueData { + return std::visit( + cppbase::overloaded{ + [](IkarusValueSchemaPrimitive const & schema) -> IkarusValueData { + switch (schema.type) { + case IkarusValuePrimitiveType_Toggle: { + return {{IkarusValueDataPrimitiveToggle{false}}}; + } + case IkarusValuePrimitiveType_Number: + return {{IkarusValueDataPrimitiveNumber{0.0}}}; + case IkarusValuePrimitiveType_Text: + return {{IkarusValueDataPrimitiveText{""}}}; + } + }, + [](IkarusValueSchemaList const & schema) -> IkarusValueData { + return {IkarusValueDataList{{}}}; + }, + [](IkarusValueSchemaMap const & schema) -> IkarusValueData { + return {IkarusValueDataMap{{}}}; + }, + [](IkarusValueSchemaTuple const & schema) -> IkarusValueData { + IkarusValueDataTuple data{}; + data.values.reserve(schema.sub_schemas.size()); + + for (auto const & sub_schema : schema.sub_schemas) { + data.values.emplace_back( + cppbase::make_owning( + sub_schema->default_value().data + ) + ); + } + + return {data}; + } + }, + variant + ); +} + +auto IkarusValueSchema::default_value() const -> IkarusValue { + return IkarusValue{*this, default_value_data()}; +} diff --git a/src/ikarus/values/schema.hpp b/src/ikarus/values/schema.hpp index 7fd07ab..e8df649 100644 --- a/src/ikarus/values/schema.hpp +++ b/src/ikarus/values/schema.hpp @@ -42,9 +42,13 @@ struct IkarusValueSchema { static auto from_json(nlohmann::json const & json) -> cppbase::Result; + static auto from_json_str(std::string_view json_str) + -> cppbase::Result; static auto to_json(IkarusValueSchema const & value) -> nlohmann::json; auto validate(IkarusValueData const & data) const -> bool; + auto default_value_data() const -> IkarusValueData; + auto default_value() const -> IkarusValue; IkarusValueSchemaVariant variant; }; diff --git a/src/ikarus/values/shared.hpp b/src/ikarus/values/shared.hpp index a4d50ac..15e6753 100644 --- a/src/ikarus/values/shared.hpp +++ b/src/ikarus/values/shared.hpp @@ -32,7 +32,7 @@ auto deserialize_enum( E min, E max ) -> cppbase::Result { - VTRY(auto iter, get_key(json, key)); + CPPBASE_VTRY(auto iter, get_key(json, key)); if (!iter->is_number_integer()) { return cppbase::err(IkarusJsonError{}); @@ -51,7 +51,7 @@ auto deserialize_enum( template auto deserialize_any(nlohmann::json const & json, std::string_view key) -> cppbase::Result { - VTRY(auto iter, get_key(json, key)); + CPPBASE_VTRY(auto iter, get_key(json, key)); try { return cppbase::ok(iter->get()); diff --git a/src/ikarus/values/value.cpp b/src/ikarus/values/value.cpp index 9a23a98..304e656 100644 --- a/src/ikarus/values/value.cpp +++ b/src/ikarus/values/value.cpp @@ -15,8 +15,8 @@ auto IkarusValue::from_json(nlohmann::json const & json) IkarusValue value{}; - VTRY(value.schema, IkarusValueSchema::from_json(json["schema"])); - VTRY(value.data, IkarusValueData::from_json(json["data"])); + CPPBASE_VTRY(value.schema, IkarusValueSchema::from_json(json["schema"])); + CPPBASE_VTRY(value.data, IkarusValueData::from_json(json["data"])); if (!value.schema.validate(value.data)) { return cppbase::err(IkarusValueParseErrorDataSchemaMismatch{}); @@ -25,6 +25,19 @@ auto IkarusValue::from_json(nlohmann::json const & json) return cppbase::ok(std::move(value)); } +auto IkarusValue::from_json_str(std::string_view json_str) + -> cppbase::Result { + auto json = nlohmann::json::parse(json_str, nullptr, false); + + if (json.is_discarded()) { + return cppbase::err( + IkarusValueDataParseError{IkarusJsonError{IkarusJsonParseError{}}} + ); + } + + return IkarusValue::from_json(json); +} + auto IkarusValue::to_json(IkarusValue const & value) -> nlohmann::json { nlohmann::json json = nlohmann::json::object(); diff --git a/src/ikarus/values/value.hpp b/src/ikarus/values/value.hpp index 74db914..cb70b1f 100644 --- a/src/ikarus/values/value.hpp +++ b/src/ikarus/values/value.hpp @@ -14,5 +14,7 @@ struct IkarusValue { static auto from_json(nlohmann::json const & json) -> cppbase::Result; + static auto from_json_str(std::string_view json_str) + -> cppbase::Result; static auto to_json(IkarusValue const & value) -> nlohmann::json; }; diff --git a/vendor/cppbase b/vendor/cppbase index e7741e0..82da677 160000 --- a/vendor/cppbase +++ b/vendor/cppbase @@ -1 +1 @@ -Subproject commit e7741e0e60f380f8b473c0881f3d8b4f23d0df91 +Subproject commit 82da6775fc56649cff6e611d6826a8e8fd321baa diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 5bfd32f..5fd5731 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 5bfd32f66164ed3d2c28003139fc5f76ef5e206d +Subproject commit 5fd5731c98d6e260e35d05a59c99822e32fe7191 From 8dff07f3fc59172f984faa0d7ed6b23b48155220 Mon Sep 17 00:00:00 2001 From: Folling Date: Sat, 4 Jan 2025 15:25:10 +0100 Subject: [PATCH 156/166] add blueprint & property implementation Signed-off-by: Folling --- include/ikarus/objects/blueprint.h | 28 +- include/ikarus/objects/property.h | 109 +++++- src/ikarus/objects/blueprint.cpp | 329 +++++++++++++++++- src/ikarus/objects/entity.cpp | 36 +- src/ikarus/objects/property.cpp | 307 ++++++++++++++++ .../migrations/m0_initial_layout.sql | 6 +- 6 files changed, 772 insertions(+), 43 deletions(-) diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 7df09a8..0631126 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -154,6 +154,22 @@ IKA_API void ikarus_blueprint_set_name( struct IkarusErrorData * error_out ); +/// \brief Gets whether a blueprint has a property. +/// \param blueprint The blueprint to check the property of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param property The property to check the existence of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \pre \li Must be in the same project as the blueprint. +/// \param error_out \see errors.h +/// \return True if the blueprint has the property, false otherwise or if an error occurs. +IKA_API bool ikarus_blueprint_has_property( + struct IkarusBlueprint * blueprint, + struct IkarusProperty * property, + struct IkarusErrorData * error_out +); + /// \brief Gets the properties of a blueprint. /// \param blueprint The blueprint to get the properties of. /// \pre \li Must not be null. @@ -161,7 +177,7 @@ IKA_API void ikarus_blueprint_set_name( /// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs. /// \param error_out \see errors.h /// \return The properties of the blueprint or null if an error occurs. -IKA_API struct IkarusBlueprint ** ikarus_blueprint_get_properties( +IKA_API struct IkarusProperty ** ikarus_blueprint_get_properties( struct IkarusBlueprint * blueprint, size_t * size_out, struct IkarusErrorData * error_out @@ -176,15 +192,15 @@ IKA_API struct IkarusBlueprint ** ikarus_blueprint_get_properties( IKA_API size_t ikarus_blueprint_get_properties_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); -/// \brief Gets all entities linked to a blueprint. -/// \param blueprint The blueprint to get the entities of. +/// \brief Gets the linked entities of a blueprint. +/// \param blueprint The blueprint to get the linked entities of. /// \pre \li Must not be null. /// \pre \li Must exist. /// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs. /// \remark Ignore if null. /// \param error_out \see errors.h -/// \return The entities linked to the blueprint or null if an error occurs. -IKA_API struct IkarusEntity ** ikarus_blueprint_get_entities( +/// \return The linked entities of the blueprint or null if an error occurs. +IKA_API struct IkarusEntity ** ikarus_blueprint_get_linked_entities( struct IkarusBlueprint * blueprint, size_t * size_out, struct IkarusErrorData * error_out @@ -197,7 +213,7 @@ IKA_API struct IkarusEntity ** ikarus_blueprint_get_entities( /// \param error_out \see errors.h /// \return The number of entities linked to the blueprint or 0 if an error occurs. IKA_API size_t -ikarus_blueprint_get_entities_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); +ikarus_blueprint_get_linked_entities_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index 2124423..0a6c3d1 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -53,23 +53,47 @@ enum IkarusPropertyCreateFlags { /// \param project The project to create the property in. /// \pre \li Must not be null. /// \pre \li Must exist. +/// \param blueprint The blueprint to create the property for. +/// \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. +/// \pre \li Must be a valid JSON buffer for a IkarusValueSchema. \see schema.h +/// \param default_value The default value of the property. +/// \pre \li Must not be null. +/// \pre \li Must be a valid JSON buffer for an IkarusValueData. \see data.h /// \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, + struct IkarusBlueprint * blueprint, char const * name, - struct IkarusValueSchema * schema, + char const * schema, + char const * default_value, IkarusPropertyCreateFlags flags, IkarusErrorData * error_out ); +/// \brief Flags for copying a property. +enum IkarusPropertyCopyFlags { + /// \brief No flags. + IkarusPropertyCopyFlags_None = 0, +}; + +/// \brief Copy a property. +/// \param property The property to copy. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param flags Flags for copying the property. +/// \param error_out \see errors.h +/// \return The copied property or NULL if an error occurred. +IKA_API IkarusProperty * +ikarus_property_copy(IkarusProperty * property, IkarusPropertyCopyFlags flags, IkarusErrorData * error_out); + /// \brief Flags for deleting a property. enum IkarusPropertyDeleteFlags { /// \brief No flags. @@ -101,15 +125,6 @@ IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty * prop /// \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. @@ -133,6 +148,78 @@ IKA_API void ikarus_property_set_name( 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 in JSON format or null if an error occurred. \see schema.h +IKA_API char const * ikarus_property_get_schema(IkarusProperty * property, IkarusErrorData * error_out); + +/// \brief Flags for setting the schema of a property. +enum IkarusPropertySetSchemaFlags { + /// \brief No flags. + IkarusPropertySetSchemaFlags_None = 0, +}; + +/// \brief Set the schema of a property. +/// \details Setting the schema of a property will reset all existing values. +/// \param property The property to set the schema of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param schema The new schema of the property. +/// \pre \li Must not be null. +/// \pre \li Must be a valid JSON buffer for a IkarusValueSchema. \see schema.h +/// \param new_default_value The new default value of the property. +/// \pre \li Must not be null. +/// \pre \li Must be a valid JSON buffer for an IkarusValueData. \see data.h +/// \param flags Flags for setting the schema of the property. +/// \param error_out \see errors.h +IKA_API void ikarus_property_set_schema( + IkarusProperty * property, + char const * schema, + char const * new_default_value, + IkarusPropertySetSchemaFlags flags, + IkarusErrorData * error_out +); + +/// \brief Get the default value of a property. +/// \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 data of the property in JSON format or null if an error occurred. \see data.h +IKA_API char const * ikarus_property_get_default_value(IkarusProperty * property, IkarusErrorData * error_out); + +/// \details The default value must match the schema of the property. + +/// \brief Flags for setting the default value of a property. +enum IkarusPropertySetDefaultValueFlags { + /// \brief No flags. + IkarusPropertySetDefaultValueFlags_None = 0, +}; + +/// \brief Flags for setting the default value of a property. +/// \param property The property to set the default value of. +/// \pre \li Must not be null. +/// \pre \li Must exist. +/// \param value The new default value of the property. +/// \pre \li Must not be null. +/// \pre \li Must be valid JSON \see data.h +/// \pre \li Must be valid according to the property's schema. +/// \param flags Flags for setting the default value of the property. +/// \param error_out \see errors.h +IKA_API void ikarus_property_set_default_value( + IkarusProperty * property, + char const * value, + IkarusPropertySetDefaultValueFlags flags, + IkarusErrorData * error_out +); + +/// \brief Gets all values for a property. +/// \param property The property to get the values of. +/// + IKARUS_END_HEADER /// @} diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index ba9e7b1..e02e07f 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -3,12 +3,27 @@ #include #include #include +#include #include IkarusBlueprint::IkarusBlueprint(struct IkarusProject * project, int64_t id): project{project}, id{id} {} +bool ikarus_blueprint_exists(IkarusBlueprint * blueprint, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(blueprint, false); + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check whether blueprint exists: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db + ->query_one("SELECT EXISTS(SELECT 1 FROM `blueprints` WHERE `id` = ?)", blueprint->id) + ); + + return exists; +} + IkarusBlueprint * ikarus_blueprint_create( struct IkarusProject * project, char const * name, @@ -35,17 +50,14 @@ IkarusBlueprint * ikarus_blueprint_create_from_entity( IkarusBlueprintCreateFromEntityFlags flags, struct IkarusErrorData * error_out ) { - IKARUS_TRYRV_OR_FAIL( - nullptr, - "{}", - IkarusErrorInfo_Database_QueryFailed, - ikarus_libikarus_func_call_to_result( - error_out, - ikarus_must_return_true(ikarus_entity_exists, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent), - entity - ) - ); IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + IKARUS_ASCERTAIN( + nullptr, + "entity must not be null", + IkarusErrorInfo_Client_InvalidInput, + ikarus_entity_exists, + entity + ); IKARUS_VTRYRV_OR_FAIL( auto id, @@ -59,15 +71,308 @@ IkarusBlueprint * ikarus_blueprint_create_from_entity( CPPBASE_TRY(entity->project->db->execute( "INSERT INTO `properties`(`blueprint`, `name`, `schema`) " - "SELECT ?, `name`, json_extract(`value`, '$.schema') FROM `entity_values` " + "SELECT ?, `name`, json_extract(`value`, '$.schema') AS `schema` FROM `entity_values` " "WHERE `entity` = ?", id, entity->id )) - return cppbase::ok(entity->project->db->last_insert_rowid()); + return cppbase::ok(id); }) ); return new IkarusBlueprint{entity->project, id}; } + +struct IkarusBlueprint * ikarus_blueprint_copy( + struct IkarusBlueprint * blueprint, + IkarusBlueprintCopyFlags flags, + struct IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VTRYRV_OR_FAIL( + auto id, + nullptr, + "failed to copy blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->transact( + [blueprint](auto * db) -> cppbase::Result { + CPPBASE_TRY(db->execute( + "INSERT INTO `blueprints`(`name`) " + "SELECT `name` FROM `blueprints` WHERE `id` = ?", + blueprint->id + )); + + auto const id = db->last_insert_rowid(); + + CPPBASE_TRY(blueprint->project->db->execute( + "INSERT INTO `properties`(`blueprint`, `name`, `schema`) " + "SELECT ?, `name`, `schema` FROM `properties` " + "WHERE `blueprint` = ?", + id, + blueprint->id + )); + + return cppbase::ok(id); + } + ) + ); + + return new IkarusBlueprint{blueprint->project, id}; +} + +void ikarus_blueprint_delete( + struct IkarusBlueprint * blueprint, + IkarusBlueprintDeleteFlags flags, + IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to delete blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->execute("DELETE FROM `blueprints` WHERE `id` = ?", blueprint->id) + ); + + delete blueprint; +} + +struct IkarusProject * +ikarus_blueprint_get_project(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + return blueprint->project; +} + +char const * ikarus_blueprint_get_name(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VTRYRV_OR_FAIL( + auto name, + nullptr, + "failed to get name for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_one("SELECT `name` FROM `blueprints` WHERE `id` = ?", blueprint->id) + ); + + return name; +} + +void ikarus_blueprint_set_name( + struct IkarusBlueprint * blueprint, + char const * name, + IkarusBlueprintSetNameFlags flags, + struct IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set name for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->execute("UPDATE `blueprints` SET `name` = ? WHERE `id` = ?", name, blueprint->id) + ); +} + +bool ikarus_blueprint_has_property( + struct IkarusBlueprint * blueprint, + struct IkarusProperty * property, + struct IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + false, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_ASCERTAIN( + false, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_FAIL_IF( + blueprint->project != property->project, + false, + "property does not belong to blueprint's project", + IkarusErrorInfo_Client_NotLinked + ); + + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check if blueprint has property: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_one( + "SELECT EXISTS(SELECT 1 FROM `properties` WHERE `blueprint` = ? AND `id` = ?)", + blueprint->id, + property->id + ) + ); + + return exists; +} + +struct IkarusProperty ** ikarus_blueprint_get_properties( + struct IkarusBlueprint * blueprint, + size_t * size_out, + struct IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VCALL(auto count, nullptr, ikarus_blueprint_get_properties_count, blueprint); + + std::int64_t ids[count]; + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to get properties for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_many_buffered( + "SELECT `id` FROM `properties` WHERE `blueprint` = ?", + ids, + count, + blueprint->id + ) + ); + + auto properties = new IkarusProperty *[count]; + std::transform(ids, ids + count, properties, [project = blueprint->project](auto id) { + return new IkarusProperty{project, id}; + }); + + if (size_out) { + *size_out = count; + } + + return properties; +} + +size_t ikarus_blueprint_get_properties_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + 0, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VTRYRV_OR_FAIL( + auto count, + 0, + "failed to get properties count for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db + ->query_one("SELECT COUNT(*) FROM `properties` WHERE `blueprint` = ?", blueprint->id) + ); + + return count; +} + +struct IkarusEntity ** ikarus_blueprint_get_linked_entities( + struct IkarusBlueprint * blueprint, + size_t * size_out, + struct IkarusErrorData * error_out +) { + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VCALL(auto count, nullptr, ikarus_blueprint_get_linked_entities_count, blueprint); + + std::int64_t ids[count]; + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to get entities for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_many_buffered( + "SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?", + ids, + count, + blueprint->id + ) + ); + + auto entities = new IkarusEntity *[count]; + std::transform(ids, ids + count, entities, [project = blueprint->project](auto id) { + return new IkarusEntity{project, id}; + }); + + if (size_out) { + *size_out = count; + } + + return entities; +} + +size_t +ikarus_blueprint_get_linked_entities_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + 0, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VTRYRV_OR_FAIL( + auto count, + 0, + "failed to get linked entities count for blueprint: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->query_one( + "SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?", + blueprint->id + ) + ); + + return count; +} diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index aae7564..ad61368 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -80,6 +80,8 @@ IkarusEntity * ikarus_entity_copy(IkarusEntity * entity, IkarusEntityCopyFlags f entity->id )); + auto const id = entity->project->db->last_insert_rowid(); + CPPBASE_TRY(entity->project->db->execute( "INSERT INTO `entity_values`(`entity`, `name`, `value`) " "SELECT ?1, `name`, `value` FROM `entity_values` WHERE " @@ -101,7 +103,7 @@ IkarusEntity * ikarus_entity_copy(IkarusEntity * entity, IkarusEntityCopyFlags f entity->id )) - return cppbase::ok(entity->project->db->last_insert_rowid()); + return cppbase::ok(id); }) ); @@ -189,8 +191,8 @@ bool ikarus_entity_is_linked_to_blueprint( struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) { - IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); - IKARUS_VCALL(auto count, false, ikarus_entity_get_linked_blueprints_count, entity); + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_VCALL(auto count, nullptr, ikarus_entity_get_linked_blueprints_count, entity); std::int64_t ids[count]; @@ -495,7 +497,7 @@ bool ikarus_entity_has_property_value( IkarusEntityPropertyValue * ikarus_entity_get_property_values(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) { - IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_VTRYRV_OR_FAIL( auto values_plain, @@ -503,9 +505,12 @@ ikarus_entity_get_property_values(IkarusEntity * entity, size_t * size_out, Ikar "failed to get property values for entity: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_many( - "SELECT `e`.`property`, IFNULL(`e`.`value`, ikarus_default_value(`p`.`schema`)) FROM `entity_property_values` AS `e` " - "INNER JOIN `properties` AS `p` ON `p`.`id` = `e`.`property` " - " WHERE `e`.`entity` = ?", + "SELECT `p`.`id`, IFNULL(`v`.`value`, ikarus_default_value(`p`.`schema`)) " + "FROM `entities` AS `e` " + "INNER JOIN `entity_blueprint_links` as `l` ON `l`.`entity` = `e`.`id` " + "INNER JOIN `properties` AS `p` ON `p`.`blueprint` = `l`.`blueprint` " + "LEFT JOIN `entity_property_values` AS `v` ON `e`.`entity` = `e`.`id` " + "WHERE `e`.`entity` = ?", entity->id ) ); @@ -527,9 +532,9 @@ ikarus_entity_get_property_values(IkarusEntity * entity, size_t * size_out, Ikar char const * ikarus_entity_get_property_value(IkarusEntity * entity, struct IkarusProperty * property, IkarusErrorData * error_out) { - IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); + IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity); IKARUS_ASCERTAIN( - false, + nullptr, "property doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_property_exists, @@ -543,6 +548,15 @@ ikarus_entity_get_property_value(IkarusEntity * entity, struct IkarusProperty * IkarusErrorInfo_Client_NotLinked ); + IKARUS_ASCERTAIN( + nullptr, + "entity doesn't have property value", + IkarusErrorInfo_Client_NotLinked, + ikarus_entity_has_property_value, + entity, + property + ); + IKARUS_VTRYRV_OR_FAIL( auto value, nullptr, @@ -567,6 +581,8 @@ void ikarus_entity_set_property_value( IkarusEntitySetPropertyValueFlags flags, IkarusErrorData * error_out ) { + IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + IKARUS_ASCERTAIN( IKARUS_VOID_RETURN, "entity doesn't exist", @@ -581,7 +597,6 @@ void ikarus_entity_set_property_value( ikarus_property_exists, property ); - IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); IKARUS_FAIL_IF( entity->project != property->project, @@ -650,6 +665,7 @@ void ikarus_entity_clear_property_value( "property does not belong to entity's project", IkarusErrorInfo_Client_NotLinked ); + IKARUS_ASCERTAIN( IKARUS_VOID_RETURN, "entity doesn't have property", diff --git a/src/ikarus/objects/property.cpp b/src/ikarus/objects/property.cpp index 43fac96..f6e8eb7 100644 --- a/src/ikarus/objects/property.cpp +++ b/src/ikarus/objects/property.cpp @@ -2,9 +2,316 @@ #include #include +#include #include #include IkarusProperty::IkarusProperty(struct IkarusProject * project, int64_t id): project{project}, id{id} {} + +bool ikarus_property_exists(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(property, false); + IKARUS_VTRYRV_OR_FAIL( + auto exists, + false, + "failed to check whether property exists: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->query_one("SELECT EXISTS(SELECT 1 FROM `properties` WHERE `id` = ?)", property->id) + ); + + return exists; +} + +IkarusProperty * ikarus_property_create( + struct IkarusBlueprint * blueprint, + char const * name, + char const * schema, + char const * default_value, + IkarusPropertyCreateFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(blueprint, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_ASCERTAIN( + nullptr, + "blueprint doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_blueprint_exists, + blueprint + ); + + IKARUS_VTRYRV_OR_FAIL( + auto schema_parsed, + nullptr, + "cannot parse schema as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValueSchema::from_json_str(schema) + ); + + IKARUS_VTRYRV_OR_FAIL( + auto default_value_parsed, + nullptr, + "cannot parse default value as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValueData::from_json_str(default_value) + ); + + IKARUS_FAIL_IF( + !schema_parsed.validate(default_value_parsed), + nullptr, + "default value is invalid for schema", + IkarusErrorInfo_Client_InvalidInput + ); + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to create property: {}", + IkarusErrorInfo_Database_QueryFailed, + blueprint->project->db->execute( + "INSERT INTO `properties`(`blueprint`, `name`, `schema`, `default_value`) VALUES(?, ?, ?, ?)", + blueprint->id, + name, + IkarusValueSchema::to_json(schema_parsed).dump(), + IkarusValueData::to_json(default_value_parsed).dump() + ) + ); + + auto const id = blueprint->project->db->last_insert_rowid(); + return new IkarusProperty{blueprint->project, id}; +} + +IkarusProperty * +ikarus_property_copy(IkarusProperty * property, IkarusPropertyCopyFlags flags, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_TRYRV_OR_FAIL( + nullptr, + "failed to copy property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->execute( + "INSERT INTO `properties`(`blueprint`, `name`, `schema`, `default_value`) " + "SELECT `blueprint`, `name`, `schema`, `default_value` FROM `properties` WHERE `id` = ?", + property->id + ) + ); + + auto const id = property->project->db->last_insert_rowid(); + return new IkarusProperty{property->project, id}; +} + +void ikarus_property_delete(IkarusProperty * property, IkarusPropertyDeleteFlags flags, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to delete property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->execute("DELETE FROM `properties` WHERE `id` = ?", property->id) + ); + + delete property; +} + +struct IkarusProject * ikarus_property_get_project(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + return property->project; +} + +char const * ikarus_property_get_name(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_VTRYRV_OR_FAIL( + auto name, + nullptr, + "failed to get name for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->query_one("SELECT `name` FROM `properties` WHERE `id` = ?", property->id) + ); + + return name; +} + +void ikarus_property_set_name( + IkarusProperty * property, + char const * name, + IkarusPropertySetNameFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set name for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->execute("UPDATE `properties` SET `name` = ? WHERE `id` = ?", name, property->id) + ); +} + +char const * ikarus_property_get_schema(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_VTRYRV_OR_FAIL( + auto schema, + nullptr, + "failed to get schema for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->query_one("SELECT `schema` FROM `properties` WHERE `id` = ?", property->id) + ); + + return schema; +} + +void ikarus_property_set_schema( + IkarusProperty * property, + char const * schema, + char const * new_default_value, + IkarusPropertySetSchemaFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(schema, IKARUS_VOID_RETURN); + IKARUS_FAIL_IF_NULL(new_default_value, IKARUS_VOID_RETURN); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_VTRYRV_OR_FAIL( + auto schema_parsed, + IKARUS_VOID_RETURN, + "cannot parse schema as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValueSchema::from_json_str(schema) + ); + + IKARUS_VTRYRV_OR_FAIL( + auto default_value_parsed, + IKARUS_VOID_RETURN, + "cannot parse default value as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValueData::from_json_str(new_default_value) + ); + + IKARUS_FAIL_IF( + !schema_parsed.validate(default_value_parsed), + IKARUS_VOID_RETURN, + "default value is invalid for schema", + IkarusErrorInfo_Client_InvalidInput + ); + + // transact updating the property and all of its associated values + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set schema for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->transact([&](auto * db) -> cppbase::Result { + CPPBASE_TRY(db->execute( + "UPDATE `properties` SET `schema` = ? WHERE `id` = ?", + IkarusValueSchema::to_json(schema_parsed).dump(), + property->id + )); + + CPPBASE_TRY(db->execute( + "UPDATE `entity_property_values` SET `value` = ? WHERE `property` = ?", + IkarusValueData::to_json(default_value_parsed).dump(), + property->id + )); + + return cppbase::ok(); + }) + ); +} + +char const * ikarus_property_get_default_value(IkarusProperty * property, IkarusErrorData * error_out) { + IKARUS_ASCERTAIN( + nullptr, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_VTRYRV_OR_FAIL( + auto default_value, + nullptr, + "failed to get default value for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db + ->query_one("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id) + ); + + return default_value; +} + +void ikarus_property_set_default_value( + IkarusProperty * property, + char const * value, + IkarusPropertySetDefaultValueFlags flags, + IkarusErrorData * error_out +) { + IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); + + IKARUS_ASCERTAIN( + IKARUS_VOID_RETURN, + "property doesn't exist", + IkarusErrorInfo_Client_NonExistent, + ikarus_property_exists, + property + ); + + IKARUS_VTRYRV_OR_FAIL( + auto value_parsed, + IKARUS_VOID_RETURN, + "cannot parse value as JSON: {}", + IkarusErrorInfo_Client_InvalidInput, + IkarusValueData::from_json_str(value) + ); + + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to set default value for property: {}", + IkarusErrorInfo_Database_QueryFailed, + property->project->db->execute( + "UPDATE `properties` SET `default_value` = ? WHERE `id` = ?", + IkarusValueData::to_json(value_parsed).dump(), + property->id + ) + ); +} diff --git a/src/ikarus/persistence/migrations/m0_initial_layout.sql b/src/ikarus/persistence/migrations/m0_initial_layout.sql index 50fb7f0..408eda1 100644 --- a/src/ikarus/persistence/migrations/m0_initial_layout.sql +++ b/src/ikarus/persistence/migrations/m0_initial_layout.sql @@ -10,8 +10,7 @@ CREATE TABLE `entity_values` `name` TEXT NOT NULL, `value` TEXT NOT NULL, - PRIMARY KEY (`id`), - UNIQUE (`entity`, `name`) + PRIMARY KEY (`entity`, `name`) ) STRICT; CREATE TABLE `blueprints` @@ -44,6 +43,5 @@ CREATE TABLE `entity_property_values` `property` INTEGER NOT NULL REFERENCES `properties` (`id`) ON DELETE CASCADE, `value` TEXT NOT NULL, - PRIMARY KEY (`id`), - UNIQUE (`entity`, `property`) + PRIMARY KEY (`entity`, `property`) ) STRICT; From 66309bd4710f5a567c8ef9728553a80cad182b6f Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 10:31:07 +0100 Subject: [PATCH 157/166] rename to libikarus and remove default cmake prefix Signed-off-by: Folling --- CMakeLists.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 63b949c..a007d4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,13 +12,14 @@ add_subdirectory(src) find_package(Boost CONFIG COMPONENTS system filesystem REQUIRED) add_library( - ikarus SHARED + libikarus SHARED ${INCLUDE_FILES} ${SOURCE_FILES} ) set_target_properties( - ikarus PROPERTIES + libikarus PROPERTIES + PREFIX "" CXX_STANDARD 23 CXX_STANDARD_REQUIRED ON LINKER_LANGUAGE CXX @@ -26,7 +27,7 @@ set_target_properties( ) target_include_directories( - ikarus + libikarus PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include PRIVATE @@ -35,7 +36,7 @@ target_include_directories( ) target_link_libraries( - ikarus PRIVATE + libikarus PRIVATE cppbase sqlitecpp nlohmann_json::nlohmann_json @@ -46,9 +47,9 @@ if (LIBIKARUS_ENABLE_LINTS) find_program(IWYU_PATH NAMES include-what-you-use iwyu REQUIRED) find_program(CLANG_TIDY_PATH NAMES clang-tidy REQUIRED) - set_property(TARGET ikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) + set_property(TARGET libikarus PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH}) set_property( - TARGET ikarus + TARGET libikarus PROPERTY CXX_CLANG_TIDY ${CLANG_TIDY_PATH}; ) endif () @@ -64,7 +65,7 @@ if (LIBIKARUS_BUILD_DOCS) ) add_dependencies( - ikarus + libikarus libikarus_docs ) endif () From b884deab07975c4832aef110070b84da770fce9f Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 12:15:07 +0100 Subject: [PATCH 158/166] check result from 'ikarus_default_value' SQLite function creation Signed-off-by: Folling --- src/ikarus/persistence/project.cpp | 176 ++++++++--------------------- 1 file changed, 49 insertions(+), 127 deletions(-) diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 0028486..692e8f8 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -18,11 +18,8 @@ constexpr char const * DB_PROJECT_ID_KEY = "project_id"; constexpr char const * DB_PROJECT_NAME_KEY = "project_name"; -auto create_impl( - std::string_view path, - std::string_view name, - IkarusErrorData * error_out -) -> std::unique_ptr { +auto create_impl(std::string_view path, std::string_view name, IkarusErrorData * error_out) + -> std::unique_ptr { IKARUS_VTRYRV_OR_FAIL( auto db, nullptr, @@ -65,65 +62,46 @@ auto create_impl( 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) + db->execute("INSERT INTO `metadata`(`key`, `value`) VALUES(?, ?)", DB_PROJECT_NAME_KEY, name).on_error(close_db) ); - db->create_function( - "ikarus_default_value", - 1, - SQLITE_UTF8 | SQLITE_DETERMINISTIC, + IKARUS_TRYRV_OR_FAIL( nullptr, - [](sqlite3_context * ctx, int argc, sqlite3_value ** argv) { - if (sqlite3_value_type(argv[0]) != SQLITE_TEXT) { - sqlite3_result_error( - ctx, - "expected the 'schema' parameter to be of type text", - SQLITE_MISMATCH - ); - return; + "failed to create custom `ikarus_default_value` SQLite function: {}", + IkarusErrorInfo_Database_QueryFailed, + db->create_function( + "ikarus_default_value", + 1, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + nullptr, + [](sqlite3_context * ctx, int argc, sqlite3_value ** argv) { + if (sqlite3_value_type(argv[0]) != SQLITE_TEXT) { + sqlite3_result_error(ctx, "expected the 'schema' parameter to be of type text", SQLITE_MISMATCH); + return; + } + + auto const schema_json = reinterpret_cast(sqlite3_value_text(argv[0])); + + auto schema_res = IkarusValueSchema::from_json_str(schema_json); + + if (schema_res.is_error()) { + sqlite3_result_error(ctx, "failed to parse schema", SQLITE_ERROR); + return; + } + + auto schema = std::move(schema_res).unwrap_value(); + + auto default_value_json = IkarusValue::to_json(schema.default_value()).dump(); + + sqlite3_result_text(ctx, default_value_json.data(), default_value_json.size(), SQLITE_TRANSIENT); } - - auto const schema_json = - reinterpret_cast(sqlite3_value_text(argv[0])); - - auto schema_res = IkarusValueSchema::from_json_str(schema_json); - - if (schema_res.is_error()) { - sqlite3_result_error( - ctx, - "failed to parse schema", - SQLITE_ERROR - ); - return; - } - - auto schema = std::move(schema_res).unwrap_value(); - - auto default_value_json = - IkarusValue::to_json(schema.default_value()).dump(); - - sqlite3_result_text( - ctx, - default_value_json.data(), - default_value_json.size(), - SQLITE_TRANSIENT - ); - } + ) ); return std::move(db); } -IkarusProject * ikarus_project_create( - char const * path, - char const * name, - IkarusErrorData * error_out -) { +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( @@ -147,18 +125,10 @@ IkarusProject * ikarus_project_create( 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", + 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); } if (auto db = create_impl(fs_path.c_str(), name, error_out); !db) { @@ -167,18 +137,9 @@ IkarusProject * ikarus_project_create( boost::system::error_code ec; boost::filesystem::remove(fs_path, ec); - IKARUS_FAIL_IF( - ec, - nullptr, - "failed to remove project db", - IkarusErrorInfo_Filesystem_MissingPermissions - ); + 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 - ); + IKARUS_FAIL(nullptr, "failed to insert project name into metadata: {}", IkarusErrorInfo_Database_QueryFailed); return nullptr; } else { @@ -186,10 +147,7 @@ IkarusProject * ikarus_project_create( } } -IkarusProject * ikarus_project_create_in_memory( - char const * name, - IkarusErrorData * error_out -) { +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), @@ -212,18 +170,13 @@ IkarusProject * ikarus_project_create_in_memory( nullptr, "failed to insert project name into metadata: {}", IkarusErrorInfo_Database_QueryFailed, - db->execute( - "INSERT INTO `metadata`(`key`, `value`) VALUES(?, ?)", - DB_PROJECT_NAME_KEY, - name - ) + 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) { +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), @@ -252,29 +205,19 @@ ikarus_project_open(char const * path, IkarusErrorData * error_out) { nullptr, "failed to retrieve project name from metadata: {}", IkarusErrorInfo_Database_QueryFailed, - db->query_one( - "SELECT `value` FROM `metadata` WHERE `key` = ?", - DB_PROJECT_NAME_KEY - ) + db->query_one("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 -) { +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 -) { +void ikarus_project_set_name(IkarusProject * project, char const * new_name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, IKARUS_VOID_RETURN); IKARUS_FAIL_IF_NULL(new_name, IKARUS_VOID_RETURN); @@ -289,18 +232,11 @@ void ikarus_project_set_name( IKARUS_VOID_RETURN, "failed to update project name: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->execute( - "UPDATE `metadata` SET `value` = ? WHERE `key` = ?", - new_name, - DB_PROJECT_NAME_KEY - ) + 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 -) { +char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); return project->path.c_str(); @@ -323,18 +259,11 @@ void ikarus_project_get_entities( IKARUS_VOID_RETURN, "unable to fetch project entities from database: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered( - "SELECT `id` FROM `entities`", - ids_out, - ids_out_size - ) + project->db->query_many_buffered("SELECT `id` FROM `entities`", ids_out, ids_out_size) ); } -size_t ikarus_project_get_entity_count( - IkarusProject const * project, - IkarusErrorData * error_out -) { +size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, 0); IKARUS_VTRYRV_OR_FAIL( @@ -365,18 +294,11 @@ void ikarus_project_get_blueprints( , "unable to fetch project blueprints from database: {}", IkarusErrorInfo_Database_QueryFailed, - project->db->query_many_buffered( - "SELECT `id` FROM `blueprints`", - ids_out, - ids_out_size - ) + project->db->query_many_buffered("SELECT `id` FROM `blueprints`", ids_out, ids_out_size) ); } -size_t ikarus_project_get_blueprint_count( - IkarusProject const * project, - IkarusErrorData * error_out -) { +size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, 0); IKARUS_VTRYRV_OR_FAIL( From bd399769db08fe9b41f8b231ee0f375a7fa34869 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 12:19:22 +0100 Subject: [PATCH 159/166] add modulemap Signed-off-by: Folling --- include/module.modulemap | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 include/module.modulemap diff --git a/include/module.modulemap b/include/module.modulemap new file mode 100644 index 0000000..72846a4 --- /dev/null +++ b/include/module.modulemap @@ -0,0 +1,13 @@ +module libikarus { + header "ikarus/objects/blueprint.h" + header "ikarus/objects/entity.h" + header "ikarus/objects/property.h" + header "ikarus/persistence/project.h" + header "ikarus/values/data.h" + header "ikarus/values/schema.h" + header "ikarus/values/value.h" + header "ikarus/errors.h" + header "ikarus/macros.h" + header "ikarus/stdtypes.h" + export * +} \ No newline at end of file From 3ecf96f9ba6d786fd206d30ef4ba8b72b3f11709 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 13:52:06 +0100 Subject: [PATCH 160/166] update cppbase Signed-off-by: Folling --- vendor/cppbase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/cppbase b/vendor/cppbase index 82da677..f547f1c 160000 --- a/vendor/cppbase +++ b/vendor/cppbase @@ -1 +1 @@ -Subproject commit 82da6775fc56649cff6e611d6826a8e8fd321baa +Subproject commit f547f1cc0ac7bfa493d9c2c22c8222f02e67c1a5 From 1990c950d9a4c2aaa8b6a66d1a9cb891cbc210c1 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 14:26:55 +0100 Subject: [PATCH 161/166] fixup IKA_API os detection Signed-off-by: Folling --- include/ikarus/macros.h | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/include/ikarus/macros.h b/include/ikarus/macros.h index e7714dc..a54809b 100644 --- a/include/ikarus/macros.h +++ b/include/ikarus/macros.h @@ -1,23 +1,28 @@ #pragma once -#define IKA_API +#if defined(_WIN32) + #define IKA_OS_FAMILY_WINDOWS + #define IKA_OS_WIN + #define IKA_API __declspec(dllexport) +#else + #define IKA_OS_FAMILY_UNIX + #define IKA_API __attribute__((visibility("default"))) -#if defined(__unix__) - #define IKA_OS_FAMILY_UNIX - #define IKA_API __attribute__((visibility("default"))) + #if defined(linux) + #define IKA_OS_LINUX + #elif defined(__APPLE__) + #define IKA_OS_MAC + #endif +#endif - #if defined(linux) - #define IKA_OS_LINUX - #endif -#elif defined(_WIN32) - #define IKA_OS_WIN - #define IKA_API __declspec(dllexport) +#ifndef IKA_API + #define IKA_API #endif #ifdef __cplusplus - #define IKARUS_BEGIN_HEADER extern "C" { - #define IKARUS_END_HEADER } + #define IKARUS_BEGIN_HEADER extern "C" { + #define IKARUS_END_HEADER } #else - #define IKARUS_BEGIN_HEADER - #define IKARUS_END_HEADER + #define IKARUS_BEGIN_HEADER + #define IKARUS_END_HEADER #endif From 12964eee2a92b409c6a75ef8442db2ac4c7d40c8 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 14:30:16 +0100 Subject: [PATCH 162/166] hide symbols by default Signed-off-by: Folling --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a007d4e..51c30ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,8 @@ set_target_properties( POSITION_INDEPENDENT_CODE TRUE ) +target_compile_options(libikarus PRIVATE "-fvisibility=hidden" "-fvisibility-inlines-hidden") + target_include_directories( libikarus PUBLIC From 6e964c465a1cca269e075e451276fbe3169b4682 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 16:25:40 +0100 Subject: [PATCH 163/166] address crossplatform (linux) incompatibilities Signed-off-by: Folling --- CMakeLists.txt | 2 +- src/ikarus/persistence/project.cpp | 4 +++- vendor/cppbase | 2 +- vendor/sqlitecpp | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51c30ed..6af3d4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.31) +cmake_minimum_required(VERSION 3.30) project(ikarus) option(LIBIKARUS_ENABLE_TESTS "Enable tests" OFF) diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 692e8f8..8dfb3ed 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -3,7 +3,9 @@ #include #include -#include +#include +#include +#include #include diff --git a/vendor/cppbase b/vendor/cppbase index f547f1c..5b3c517 160000 --- a/vendor/cppbase +++ b/vendor/cppbase @@ -1 +1 @@ -Subproject commit f547f1cc0ac7bfa493d9c2c22c8222f02e67c1a5 +Subproject commit 5b3c5172d2ba1c10b40df9bcf06a588da2f55cc7 diff --git a/vendor/sqlitecpp b/vendor/sqlitecpp index 5fd5731..5eaa8c3 160000 --- a/vendor/sqlitecpp +++ b/vendor/sqlitecpp @@ -1 +1 @@ -Subproject commit 5fd5731c98d6e260e35d05a59c99822e32fe7191 +Subproject commit 5eaa8c3e76a21895bef4ed55bf288533dc31cf2a From 8fbb8910242c1ee5fa72730f3e0355a507af9237 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 18:42:39 +0100 Subject: [PATCH 164/166] add missing flags in project funcs Signed-off-by: Folling --- src/ikarus/errors.cpp | 35 ++++++++----------------- src/ikarus/persistence/project.cpp | 42 +++++++++++++++--------------- 2 files changed, 32 insertions(+), 45 deletions(-) diff --git a/src/ikarus/errors.cpp b/src/ikarus/errors.cpp index 07f7a15..b8dcc7d 100644 --- a/src/ikarus/errors.cpp +++ b/src/ikarus/errors.cpp @@ -4,11 +4,7 @@ #include -void safe_strcpy( - std::string_view const src, - char * dest, - std::size_t const dest_size -) { +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') { @@ -21,7 +17,7 @@ void safe_strcpy( dest[i] = '\0'; } -char const * ikarus_get_error_info_name(IkarusErrorInfo info) { +char const * ikarus_error_info_get_name(IkarusErrorInfo info) { switch (info) { case IkarusErrorInfo_None: return "None"; @@ -29,34 +25,25 @@ char const * ikarus_get_error_info_name(IkarusErrorInfo info) { case IkarusErrorInfo_Client_InvalidInput: return "Client::InvalidInput"; case IkarusErrorInfo_Client_NonExistent: return "Client::NonExistent"; case IkarusErrorInfo_Client_InvalidFormat: return "Client::InvalidFormat"; - case IkarusErrorInfo_Client_ConstraintViolated: - return "Client::ConstraintViolated"; + case IkarusErrorInfo_Client_ConstraintViolated: return "Client::ConstraintViolated"; case IkarusErrorInfo_Filesystem_NotFound: return "Filesystem::NotFound"; - case IkarusErrorInfo_Filesystem_AlreadyExists: - return "Filesystem::AlreadyExists"; - case IkarusErrorInfo_Filesystem_MissingPermissions: - return "Filesystem::MissingPermissions"; - case IkarusErrorInfo_Filesystem_InsufficientSpace: - return "Filesystem::InsufficientSpace"; - case IkarusErrorInfo_Filesystem_InvalidPath: - return "Filesystem::InvalidPath"; + case IkarusErrorInfo_Filesystem_AlreadyExists: return "Filesystem::AlreadyExists"; + case IkarusErrorInfo_Filesystem_MissingPermissions: 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: return "Database::ConnectionFailed"; case IkarusErrorInfo_Database_QueryFailed: return "Database::QueryFailed"; - case IkarusErrorInfo_Database_MigrationFailed: - return "Database::MigrationFailed"; + case IkarusErrorInfo_Database_MigrationFailed: return "Database::MigrationFailed"; case IkarusErrorInfo_Database_InvalidState: return "Database::InvalidState"; case IkarusErrorInfo_OS_SystemCallFailed: return "OS::SystemCallFailed"; case IkarusErrorInfo_OS_InvalidReturnValue: return "OS::InvalidReturnValue"; case IkarusErrorInfo_OS_InsufficientMemory: return "OS::InsufficientMemory"; - case IkarusErrorInfo_LibIkarus_InvalidState: - return "LibIkarus::InvalidState"; - case IkarusErrorInfo_LibIkarus_CannotPerformOperation: - return "LibIkarus::CannotPerformOperation"; + case IkarusErrorInfo_LibIkarus_InvalidState: return "LibIkarus::InvalidState"; + case IkarusErrorInfo_LibIkarus_CannotPerformOperation: return "LibIkarus::CannotPerformOperation"; case IkarusErrorInfo_LibIkarus_Timeout: return "LibIkarus::Timeout"; default: return "Invalid"; diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 8dfb3ed..2501eed 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -149,7 +149,11 @@ IkarusProject * ikarus_project_create(char const * path, char const * name, Ikar } } -IkarusProject * ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out) { +IkarusProject * ikarus_project_create_in_memory( + char const * name, + IkarusProjectCreateInMemoryFlags flags, + IkarusErrorData * error_out +) { IKARUS_FAIL_IF_NULL(name, nullptr); IKARUS_FAIL_IF( cppbase::is_empty_or_blank(name), @@ -178,7 +182,7 @@ IkarusProject * ikarus_project_create_in_memory(char const * name, IkarusErrorDa } } -IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_out) { +IkarusProject * ikarus_project_open(char const * path, IkarusProjectOpenFlags flags, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(path, nullptr); IKARUS_FAIL_IF( cppbase::is_empty_or_blank(path), @@ -213,31 +217,27 @@ IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_o return new IkarusProject{name, path, std::move(db)}; } +void ikarus_project_close(struct IkarusProject * project, IkarusProjectCloseFlags flags, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, IKARUS_VOID_RETURN); + + if (project->db) { + IKARUS_TRYRV_OR_FAIL( + IKARUS_VOID_RETURN, + "failed to close project db: {}", + IkarusErrorInfo_Database_QueryFailed, + project->db->close() + ); + } + + delete project; +} + 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_VOID_RETURN); - IKARUS_FAIL_IF_NULL(new_name, IKARUS_VOID_RETURN); - - IKARUS_FAIL_IF( - cppbase::is_empty_or_blank(new_name), - IKARUS_VOID_RETURN, - "name must not be empty", - IkarusErrorInfo_Client_InvalidInput - ); - - IKARUS_TRYRV_OR_FAIL( - IKARUS_VOID_RETURN, - "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); From 1a339f0a57b15aed5018ba98d33c7eebc2c694b7 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 5 Jan 2025 18:48:24 +0100 Subject: [PATCH 165/166] improve C header compatibility Signed-off-by: Folling --- include/ikarus/errors.h | 5 +- include/ikarus/objects/blueprint.h | 28 +++--- include/ikarus/objects/entity.h | 131 ++++++++++++--------------- include/ikarus/objects/property.h | 59 +++++++----- include/ikarus/persistence/project.h | 59 +++++------- include/ikarus/stdtypes.h | 10 +- include/ikarus/values/data.h | 8 +- include/ikarus/values/schema.h | 8 +- include/ikarus/values/value.h | 8 +- 9 files changed, 151 insertions(+), 165 deletions(-) diff --git a/include/ikarus/errors.h b/include/ikarus/errors.h index eb123b0..efd2155 100644 --- a/include/ikarus/errors.h +++ b/include/ikarus/errors.h @@ -4,6 +4,7 @@ /// \author Folling #include +#include /// \addtogroup errors Errors /// \brief Error handling within libikarus @@ -116,11 +117,11 @@ IKA_API char const * ikarus_error_info_get_name(enum IkarusErrorInfo info); /// \brief Checks if an error data is a success. /// \param data The error data to check. /// \return True if the error data is a success, false otherwise. -IKA_API bool ikarus_error_data_is_success(IkarusErrorData const * data); +IKA_API bool ikarus_error_data_is_success(struct IkarusErrorData const * data); /// \brief Checks if an error data is an error. /// \param data The error data to check. /// \return True if the error data is an error, false otherwise. -IKA_API bool ikarus_error_data_is_error(IkarusErrorData const * data); +IKA_API bool ikarus_error_data_is_error(struct IkarusErrorData const * data); IKARUS_END_HEADER diff --git a/include/ikarus/objects/blueprint.h b/include/ikarus/objects/blueprint.h index 0631126..bd4bb1c 100644 --- a/include/ikarus/objects/blueprint.h +++ b/include/ikarus/objects/blueprint.h @@ -3,13 +3,17 @@ /// \file blueprint.h /// \author Folling +/// \defgroup blueprints Blueprints +/// \brief Blueprints are templates for entities. +/// @{ + #include #include #include -/// \defgroup blueprints Blueprints -/// \brief Blueprints are templates for entities. -/// @{ +struct IkarusProject; +struct IkarusEntity; +struct IkarusProperty; IKARUS_BEGIN_HEADER @@ -25,7 +29,7 @@ struct IkarusBlueprint; /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return True if the blueprint exists, false otherwise or if an error occurs. -IKA_API bool ikarus_blueprint_exists(IkarusBlueprint * blueprint, IkarusErrorData * error_out); +IKA_API bool ikarus_blueprint_exists(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out); /// \brief Flags for creating a blueprint. enum IkarusBlueprintCreateFlags { @@ -44,10 +48,10 @@ enum IkarusBlueprintCreateFlags { /// \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 struct IkarusBlueprint * ikarus_blueprint_create( struct IkarusProject * project, char const * name, - IkarusBlueprintCreateFlags flags, + enum IkarusBlueprintCreateFlags flags, struct IkarusErrorData * error_out ); @@ -68,10 +72,10 @@ enum IkarusBlueprintCreateFromEntityFlags { /// \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( +IKA_API struct IkarusBlueprint * ikarus_blueprint_create_from_entity( struct IkarusEntity * entity, char const * name, - IkarusBlueprintCreateFromEntityFlags flags, + enum IkarusBlueprintCreateFromEntityFlags flags, struct IkarusErrorData * error_out ); @@ -90,7 +94,7 @@ enum IkarusBlueprintCopyFlags { /// \return The copied blueprint or null if an error occurs. IKA_API struct IkarusBlueprint * ikarus_blueprint_copy( struct IkarusBlueprint * blueprint, - IkarusBlueprintCopyFlags flags, + enum IkarusBlueprintCopyFlags flags, struct IkarusErrorData * error_out ); @@ -109,8 +113,8 @@ enum IkarusBlueprintDeleteFlags { /// \param error_out \see errors.h IKA_API void ikarus_blueprint_delete( struct IkarusBlueprint * blueprint, - IkarusBlueprintDeleteFlags flags, - IkarusErrorData * error_out + enum IkarusBlueprintDeleteFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets the project a blueprint belongs to. @@ -150,7 +154,7 @@ enum IkarusBlueprintSetNameFlags { IKA_API void ikarus_blueprint_set_name( struct IkarusBlueprint * blueprint, char const * name, - IkarusBlueprintSetNameFlags flags, + enum IkarusBlueprintSetNameFlags flags, struct IkarusErrorData * error_out ); diff --git a/include/ikarus/objects/entity.h b/include/ikarus/objects/entity.h index 990be61..7b9518f 100644 --- a/include/ikarus/objects/entity.h +++ b/include/ikarus/objects/entity.h @@ -1,15 +1,19 @@ #pragma once -#include -#include -#include - /// \file entity.h /// \author Folling /// \defgroup entities Entities /// \brief Entities are the core building blocks of Ikarus. +#include +#include +#include + +struct IkarusProject; +struct IkarusEntity; +struct IkarusProperty; + IKARUS_BEGIN_HEADER /// \brief Entities are the core building blocks of Ikarus. @@ -38,8 +42,7 @@ enum IkarusEntityCreateFlags { /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return True if the entity exists, false otherwise or if an error occurs. -IKA_API bool -ikarus_entity_exists(IkarusEntity * entity, IkarusErrorData * error_out); +IKA_API bool ikarus_entity_exists(struct IkarusEntity * entity, struct IkarusErrorData * error_out); /// \brief Creates a new entity. /// \param project The project to create the entity in. @@ -51,11 +54,11 @@ ikarus_entity_exists(IkarusEntity * entity, IkarusErrorData * error_out); /// \param flags Flags for creating the entity. /// \param error_out \see errors.h /// \return The created entity or null if an error occurs. -IKA_API IkarusEntity * ikarus_entity_create( +IKA_API struct IkarusEntity * ikarus_entity_create( struct IkarusProject * project, char const * name, - IkarusEntityCreateFlags flags, - IkarusErrorData * error_out + enum IkarusEntityCreateFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for deleting an entity. @@ -71,9 +74,9 @@ enum IkarusEntityDeleteFlags { /// \param flags Flags for deleting the entity. /// \param error_out \see errors.h IKA_API void ikarus_entity_delete( - IkarusEntity * entity, - IkarusEntityDeleteFlags flags, - IkarusErrorData * error_out + struct IkarusEntity * entity, + enum IkarusEntityDeleteFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for copying an entity. @@ -89,11 +92,8 @@ enum IkarusEntityCopyFlags { /// \param flags Flags for copying the entity. /// \param error_out \see errors.h /// \return The copied entity or null if an error occurs. -IKA_API IkarusEntity * ikarus_entity_copy( - IkarusEntity * entity, - IkarusEntityCopyFlags flags, - IkarusErrorData * error_out -); +IKA_API struct IkarusEntity * +ikarus_entity_copy(struct IkarusEntity * entity, enum IkarusEntityCopyFlags flags, struct IkarusErrorData * error_out); /// \brief Gets the project an entity belongs to. /// \param entity The entity to get the project of. @@ -102,7 +102,7 @@ IKA_API IkarusEntity * ikarus_entity_copy( /// \param error_out \see errors.h /// \return The project the entity belongs to. IKA_API struct IkarusProject * -ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out); +ikarus_entity_get_project(struct IkarusEntity * entity, struct IkarusErrorData * error_out); /// \brief Gets the name of an entity. /// \param entity The entity to get the name of. @@ -110,8 +110,7 @@ ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out); /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The name of the entity. -IKA_API char const * -ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out); +IKA_API char const * ikarus_entity_get_name(struct IkarusEntity * entity, struct IkarusErrorData * error_out); /// \brief Flags for setting the name of an entity. enum IkarusEntitySetNameFlags { @@ -129,10 +128,10 @@ enum IkarusEntitySetNameFlags { /// \param flags Flags for setting the name of the entity. /// \param error_out \see errors.h IKA_API void ikarus_entity_set_name( - IkarusEntity * entity, + struct IkarusEntity * entity, char const * name, - IkarusEntitySetNameFlags flags, - IkarusErrorData * error_out + enum IkarusEntitySetNameFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets whether an entity is linked to a blueprint. @@ -146,9 +145,9 @@ IKA_API void ikarus_entity_set_name( /// \param error_out \see errors.h /// \return True if the entity is linked to the blueprint, false otherwise or if an error occurs. IKA_API bool ikarus_entity_is_linked_to_blueprint( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusBlueprint * blueprint, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Gets the blueprints an entity is linked to. @@ -159,9 +158,9 @@ IKA_API bool ikarus_entity_is_linked_to_blueprint( /// or undefined if an error occurs. /// \param error_out \see errors.h IKA_API struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( - IkarusEntity * entity, + struct IkarusEntity * entity, size_t * size_out, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Gets the number of blueprints an entity is linked to. @@ -170,10 +169,8 @@ IKA_API struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of linked blueprints or 0 if an error occurs. -IKA_API size_t ikarus_entity_get_linked_blueprints_count( - IkarusEntity * entity, - IkarusErrorData * error_out -); +IKA_API size_t +ikarus_entity_get_linked_blueprints_count(struct IkarusEntity * entity, struct IkarusErrorData * error_out); /// \brief Flags for linking an entity to a blueprint. enum IkarusEntityLinkBlueprintFlags { @@ -195,10 +192,10 @@ enum IkarusEntityLinkBlueprintFlags { /// \param flags Flags for linking the entity to the blueprint. /// \param error_out \see errors.h IKA_API void ikarus_entity_link_blueprint( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusBlueprint * blueprint, - IkarusEntityLinkBlueprintFlags flags, - IkarusErrorData * error_out + enum IkarusEntityLinkBlueprintFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for unlinking an entity from a blueprint. @@ -220,10 +217,10 @@ enum IkarusEntityUnlinkBlueprintFlags { /// \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 IkarusEntity * entity, struct IkarusBlueprint * blueprint, - IkarusEntityUnlinkBlueprintFlags flags, - IkarusErrorData * error_out + enum IkarusEntityUnlinkBlueprintFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets whether an entity has a value. @@ -234,11 +231,8 @@ IKA_API void ikarus_entity_unlink_blueprint( /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return True if the entity has a value with the name, false otherwise or if an error occurs. -IKA_API bool ikarus_entity_has_value( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out -); +IKA_API bool +ikarus_entity_has_value(struct IkarusEntity * entity, char const * name, struct IkarusErrorData * error_out); /// \brief Struct for an entity value. struct IkarusEntityValue { @@ -256,11 +250,8 @@ struct IkarusEntityValue { /// or undefined if an error occurs. /// \param error_out \see errors.h /// \return The values or null if an error occurs. -IKA_API IkarusEntityValue * ikarus_entity_get_values( - IkarusEntity * entity, - size_t * size_out, - IkarusErrorData * error_out -); +IKA_API struct IkarusEntityValue * +ikarus_entity_get_values(struct IkarusEntity * entity, size_t * size_out, struct IkarusErrorData * error_out); /// \brief Gets a value of an entity. /// \param entity The entity to get the value of. @@ -271,11 +262,8 @@ IKA_API IkarusEntityValue * ikarus_entity_get_values( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The value, in json format of or null if an error occurs. \see value.h -IKA_API char const * ikarus_entity_get_value( - IkarusEntity * entity, - char const * name, - IkarusErrorData * error_out -); +IKA_API char const * +ikarus_entity_get_value(struct IkarusEntity * entity, char const * name, struct IkarusErrorData * error_out); /// \brief Flags for setting a value of an entity. enum IkarusEntitySetValueFlags { @@ -297,11 +285,11 @@ enum IkarusEntitySetValueFlags { /// \param flags Flags for setting the value. /// \param error_out \see errors.h IKA_API void ikarus_entity_set_value( - IkarusEntity * entity, + struct IkarusEntity * entity, char const * name, char const * value, - IkarusEntitySetValueFlags flags, - IkarusErrorData * error_out + enum IkarusEntitySetValueFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for deleting a value of an entity. @@ -320,10 +308,10 @@ enum IkarusEntityDeleteValueFlags { /// \param flags Flags for deleting the value. /// \param error_out \see errors.h IKA_API void ikarus_entity_delete_value( - IkarusEntity * entity, + struct IkarusEntity * entity, char const * name, - IkarusEntityDeleteValueFlags flags, - IkarusErrorData * error_out + enum IkarusEntityDeleteValueFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets whether an entity has a property value. @@ -337,9 +325,9 @@ IKA_API void ikarus_entity_delete_value( /// \param error_out \see errors.h /// \return True if the entity has a value for the property, false otherwise or if an error occurs. IKA_API bool ikarus_entity_has_property_value( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusProperty * property, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Struct for an entity property value. @@ -358,11 +346,8 @@ struct IkarusEntityPropertyValue { /// returned array or undefined if an error occurs. /// \param error_out \see errors.h /// \return The property values, or null if an error occurs. -IKA_API IkarusEntityPropertyValue * ikarus_entity_get_property_values( - IkarusEntity * entity, - size_t * size_out, - IkarusErrorData * error_out -); +IKA_API struct IkarusEntityPropertyValue * +ikarus_entity_get_property_values(struct IkarusEntity * entity, size_t * size_out, struct IkarusErrorData * error_out); /// \brief Gets the value of a property of an entity. /// \param entity The entity to get the value of. @@ -376,9 +361,9 @@ IKA_API IkarusEntityPropertyValue * ikarus_entity_get_property_values( /// \return The value, in json format of or null if an error occurs. \see /// value.h IKA_API char const * ikarus_entity_get_property_value( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusProperty * property, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Flags for setting the value of a property of an entity. @@ -403,11 +388,11 @@ enum IkarusEntitySetPropertyValueFlags { /// \param flags Flags for setting the property value. /// \param error_out \see errors.h IKA_API void ikarus_entity_set_property_value( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusProperty * property, char const * value, - IkarusEntitySetPropertyValueFlags flags, - IkarusErrorData * error_out + enum IkarusEntitySetPropertyValueFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for clearing the value of a property of an entity. @@ -427,10 +412,10 @@ enum IkarusEntityClearPropertyValueFlags { /// \param flags Flags for clearing the property value. /// \param error_out \see errors.h IKA_API void ikarus_entity_clear_property_value( - IkarusEntity * entity, + struct IkarusEntity * entity, struct IkarusProperty * property, - IkarusEntitySetPropertyValueFlags flags, - IkarusErrorData * error_out + enum IkarusEntitySetPropertyValueFlags flags, + struct IkarusErrorData * error_out ); IKARUS_END_HEADER diff --git a/include/ikarus/objects/property.h b/include/ikarus/objects/property.h index 0a6c3d1..a24d3c3 100644 --- a/include/ikarus/objects/property.h +++ b/include/ikarus/objects/property.h @@ -3,13 +3,16 @@ /// \file property.h /// \author Folling +/// \defgroup properties Properties +/// \brief Properties define the structure and types of data. +/// @{ + #include #include #include -/// \defgroup properties Properties -/// \brief Properties define the structure and types of data. -/// @{ +struct IkarusProject; +struct IkarusBlueprint; IKARUS_BEGIN_HEADER @@ -41,7 +44,7 @@ struct IkarusProperty; /// \pre \li Must not be null. /// \param error_out \see errors.h /// \return True if the property exists, false otherwise or if an error occurs. -IKA_API bool ikarus_property_exists(IkarusProperty * property, IkarusErrorData * error_out); +IKA_API bool ikarus_property_exists(struct IkarusProperty * property, struct IkarusErrorData * error_out); /// \brief Flags for creating a property. enum IkarusPropertyCreateFlags { @@ -69,13 +72,13 @@ enum IkarusPropertyCreateFlags { /// \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( +IKA_API struct IkarusProperty * ikarus_property_create( struct IkarusBlueprint * blueprint, char const * name, char const * schema, char const * default_value, - IkarusPropertyCreateFlags flags, - IkarusErrorData * error_out + enum IkarusPropertyCreateFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for copying a property. @@ -91,8 +94,11 @@ enum IkarusPropertyCopyFlags { /// \param flags Flags for copying the property. /// \param error_out \see errors.h /// \return The copied property or NULL if an error occurred. -IKA_API IkarusProperty * -ikarus_property_copy(IkarusProperty * property, IkarusPropertyCopyFlags flags, IkarusErrorData * error_out); +IKA_API struct IkarusProperty * ikarus_property_copy( + struct IkarusProperty * property, + enum IkarusPropertyCopyFlags flags, + struct IkarusErrorData * error_out +); /// \brief Flags for deleting a property. enum IkarusPropertyDeleteFlags { @@ -104,8 +110,11 @@ enum IkarusPropertyDeleteFlags { /// \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); +IKA_API void ikarus_property_delete( + struct IkarusProperty * property, + enum IkarusPropertyDeleteFlags flags, + struct IkarusErrorData * error_out +); /// \brief Get the project a property belongs to. /// \param property The property to get the project of. @@ -114,7 +123,8 @@ ikarus_property_delete(IkarusProperty * property, IkarusPropertyDeleteFlags flag /// \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); +IKA_API struct IkarusProject * +ikarus_property_get_project(struct IkarusProperty * property, struct IkarusErrorData * error_out); /// \brief Get the name of a property. /// \param property The property to get the name of. @@ -123,7 +133,7 @@ IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty * prop /// \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); +IKA_API char const * ikarus_property_get_name(struct IkarusProperty * property, struct IkarusErrorData * error_out); /// \brief Flags for setting the name of a property. enum IkarusPropertySetNameFlags { @@ -142,10 +152,10 @@ enum IkarusPropertySetNameFlags { /// \param error_out \see errors.h /// \remark Ownership remains with the caller. IKA_API void ikarus_property_set_name( - IkarusProperty * property, + struct IkarusProperty * property, char const * name, - IkarusPropertySetNameFlags flags, - IkarusErrorData * error_out + enum IkarusPropertySetNameFlags flags, + struct IkarusErrorData * error_out ); /// \brief Get the schema of a property. @@ -154,7 +164,7 @@ IKA_API void ikarus_property_set_name( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The schema of the property in JSON format or null if an error occurred. \see schema.h -IKA_API char const * ikarus_property_get_schema(IkarusProperty * property, IkarusErrorData * error_out); +IKA_API char const * ikarus_property_get_schema(struct IkarusProperty * property, struct IkarusErrorData * error_out); /// \brief Flags for setting the schema of a property. enum IkarusPropertySetSchemaFlags { @@ -176,11 +186,11 @@ enum IkarusPropertySetSchemaFlags { /// \param flags Flags for setting the schema of the property. /// \param error_out \see errors.h IKA_API void ikarus_property_set_schema( - IkarusProperty * property, + struct IkarusProperty * property, char const * schema, char const * new_default_value, - IkarusPropertySetSchemaFlags flags, - IkarusErrorData * error_out + enum IkarusPropertySetSchemaFlags flags, + struct IkarusErrorData * error_out ); /// \brief Get the default value of a property. @@ -189,7 +199,8 @@ IKA_API void ikarus_property_set_schema( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The default value data of the property in JSON format or null if an error occurred. \see data.h -IKA_API char const * ikarus_property_get_default_value(IkarusProperty * property, IkarusErrorData * error_out); +IKA_API char const * +ikarus_property_get_default_value(struct IkarusProperty * property, struct IkarusErrorData * error_out); /// \details The default value must match the schema of the property. @@ -210,10 +221,10 @@ enum IkarusPropertySetDefaultValueFlags { /// \param flags Flags for setting the default value of the property. /// \param error_out \see errors.h IKA_API void ikarus_property_set_default_value( - IkarusProperty * property, + struct IkarusProperty * property, char const * value, - IkarusPropertySetDefaultValueFlags flags, - IkarusErrorData * error_out + enum IkarusPropertySetDefaultValueFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets all values for a property. diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index af40147..f5da0ad 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -3,17 +3,15 @@ /// \file project.h /// \author Folling -#include - -#include -#include -#include - /// \defgroup projects Projects /// \brief Projects are the persistence mechanism of Ikarus. Each project contains a set of objects. /// \details Projects are stored on the filesystem. Each project has a name. /// @{ +#include +#include +#include + IKARUS_BEGIN_HEADER /// \brief An Ikarus project. @@ -44,8 +42,8 @@ enum IkarusProjectCreateFlags { IKA_API struct IkarusProject * ikarus_project_create( char const * path, char const * name, - IkarusProjectCreateFlags flags, - IkarusErrorData * error_out + enum IkarusProjectCreateFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for creating a project in memory. @@ -64,8 +62,8 @@ enum IkarusProjectCreateInMemoryFlags { /// \remark Must be closed with #ikarus_project_close. IKA_API struct IkarusProject * ikarus_project_create_in_memory( char const * name, - IkarusProjectCreateInMemoryFlags flags, - IkarusErrorData * error_out + enum IkarusProjectCreateInMemoryFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for opening a project. @@ -82,11 +80,8 @@ enum IkarusProjectOpenFlags { /// \param error_out \see errors.h /// \return The opened project or null if an error occurs. /// \remark Must be closed with #ikarus_project_close. -IKA_API struct IkarusProject * ikarus_project_open( - char const * path, - IkarusProjectOpenFlags flags, - IkarusErrorData * error_out -); +IKA_API struct IkarusProject * +ikarus_project_open(char const * path, enum IkarusProjectOpenFlags flags, struct IkarusErrorData * error_out); /// \brief Flags for closing a project in memory. enum IkarusProjectCloseFlags { @@ -105,8 +100,8 @@ enum IkarusProjectCloseFlags { /// \remark Mutually exclusive with #ikarus_project_delete. IKA_API void ikarus_project_close( struct IkarusProject * project, - IkarusProjectCloseFlags flags, - IkarusErrorData * error_out + enum IkarusProjectCloseFlags flags, + struct IkarusErrorData * error_out ); /// \brief Flags for deleting a project. @@ -125,8 +120,8 @@ enum IkarusProjectDeleteFlags { /// \remark Mutually exclusive with #ikarus_project_close. IKA_API void ikarus_project_delete( struct IkarusProject * project, - IkarusProjectDeleteFlags flags, - IkarusErrorData * error_out + enum IkarusProjectDeleteFlags flags, + struct IkarusErrorData * error_out ); /// \brief Gets the name of a project. @@ -135,10 +130,7 @@ IKA_API void ikarus_project_delete( /// \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 -); +IKA_API char const * ikarus_project_get_name(struct IkarusProject const * project, struct IkarusErrorData * error_out); /// \brief Gets the path of a project. /// \param project The project to delete. @@ -146,10 +138,7 @@ IKA_API char const * ikarus_project_get_name( /// \pre \li Must exist. /// \param error_out \see errors.h /// \returns The path of the project or null if an error occurs. -IKA_API char const * ikarus_project_get_path( - struct IkarusProject const * project, - IkarusErrorData * error_out -); +IKA_API char const * ikarus_project_get_path(struct IkarusProject const * project, struct IkarusErrorData * error_out); /// \brief Gets all entities in a project. /// \param project The project from which to get the entities. @@ -162,7 +151,7 @@ IKA_API char const * ikarus_project_get_path( IKA_API struct IkarusEntity ** ikarus_project_get_entities( struct IkarusProject const * project, size_t * size_out, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Gets how many entities in a project there are. @@ -171,10 +160,8 @@ IKA_API struct IkarusEntity ** ikarus_project_get_entities( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The count or 0 if an error occurs. -IKA_API size_t ikarus_project_get_entities_count( - IkarusProject const * project, - IkarusErrorData * error_out -); +IKA_API size_t +ikarus_project_get_entities_count(struct IkarusProject const * project, struct IkarusErrorData * error_out); /// \brief Gets all blueprints from a project. /// \param project The project from which to get the blueprints. @@ -187,7 +174,7 @@ IKA_API size_t ikarus_project_get_entities_count( IKA_API struct IkarusBlueprint ** ikarus_project_get_blueprints( struct IkarusProject const * project, size_t * size_out, - IkarusErrorData * error_out + struct IkarusErrorData * error_out ); /// \brief Gets how many blueprints in a project there are. @@ -196,10 +183,8 @@ IKA_API struct IkarusBlueprint ** ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The count or 0 if an error occurs. -IKA_API size_t ikarus_project_get_blueprints_count( - IkarusProject const * project, - IkarusErrorData * error_out -); +IKA_API size_t +ikarus_project_get_blueprints_count(struct IkarusProject const * project, struct IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/include/ikarus/stdtypes.h b/include/ikarus/stdtypes.h index 71d07c2..3f412eb 100644 --- a/include/ikarus/stdtypes.h +++ b/include/ikarus/stdtypes.h @@ -1,11 +1,11 @@ #pragma once #ifdef __cplusplus - #include - #include + #include + #include using std::size_t; #else - #include - #include - #include + #include + #include + #include #endif diff --git a/include/ikarus/values/data.h b/include/ikarus/values/data.h index d4e2367..694e640 100644 --- a/include/ikarus/values/data.h +++ b/include/ikarus/values/data.h @@ -1,14 +1,14 @@ #pragma once -#include -#include -#include - /// \file data.h /// \author Folling /// \addtogroup values Values +#include +#include +#include + IKARUS_BEGIN_HEADER /// \brief Data stores the actual information of a value. diff --git a/include/ikarus/values/schema.h b/include/ikarus/values/schema.h index 9e25e01..73997a6 100644 --- a/include/ikarus/values/schema.h +++ b/include/ikarus/values/schema.h @@ -1,14 +1,14 @@ #pragma once -#include -#include -#include - /// \file schema.h /// \author Folling /// \addtogroup values Values +#include +#include +#include + IKARUS_BEGIN_HEADER /// \brief Schemas define the type of value. diff --git a/include/ikarus/values/value.h b/include/ikarus/values/value.h index 870f551..076b8ad 100644 --- a/include/ikarus/values/value.h +++ b/include/ikarus/values/value.h @@ -1,15 +1,15 @@ #pragma once -#include -#include -#include - /// \file value.h /// \author Folling /// \defgroup entities Entities /// \brief Entities are the core building blocks of Ikarus. +#include +#include +#include + IKARUS_BEGIN_HEADER /// \brief Values are data containers for entities. From 341c8dd00aa76b608aac43d2ead9eaf3ccda5153 Mon Sep 17 00:00:00 2001 From: Folling Date: Sun, 13 Apr 2025 16:47:23 +0200 Subject: [PATCH 166/166] remove toolchains & cmake preset Signed-off-by: Folling --- CMakePresets.json | 35 -------------------------------- tools/cmake/toolchains/mac.cmake | 5 ----- 2 files changed, 40 deletions(-) delete mode 100644 CMakePresets.json delete mode 100644 tools/cmake/toolchains/mac.cmake diff --git a/CMakePresets.json b/CMakePresets.json deleted file mode 100644 index 0772ba0..0000000 --- a/CMakePresets.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "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" - } - ] -} diff --git a/tools/cmake/toolchains/mac.cmake b/tools/cmake/toolchains/mac.cmake deleted file mode 100644 index b7120cd..0000000 --- a/tools/cmake/toolchains/mac.cmake +++ /dev/null @@ -1,5 +0,0 @@ -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")