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