diff --git a/CHANGELOG.md b/CHANGELOG.md index 5589220..c49705b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +#### v0.4.2+v0.25.0 + +---- + +- Scaffolding: Add support for errors +- Scaffolding: Add internal ref counts for object types + #### v0.4.1+v0.25.0 ---- diff --git a/bindgen/src/bindings/cpp/templates/cpp_scaffolding.cpp b/bindgen/src/bindings/cpp/templates/cpp_scaffolding.cpp index 54af1fb..83d294b 100644 --- a/bindgen/src/bindings/cpp/templates/cpp_scaffolding.cpp +++ b/bindgen/src/bindings/cpp/templates/cpp_scaffolding.cpp @@ -1,4 +1,4 @@ -{%- import "macros.cpp" as macros %} +{%- import "scaffolding/macros.cpp" as macros %} {% let namespace = ci.namespace() %} {% match config.namespace %} {% when Some with (ns) %} @@ -22,9 +22,14 @@ #include #include #include +#include using namespace {{ namespace }}; +constexpr int8_t UNIFFI_CALL_STATUS_OK = 0; +constexpr int8_t UNIFFI_CALL_STATUS_ERROR = 1; +constexpr int8_t UNIFFI_CALL_STATUS_PANIC = 2; + struct ForeignBytes { int32_t len; uint8_t *data; @@ -92,7 +97,7 @@ struct RustStream: std::basic_iostream { RustStreamBuffer streambuf; }; -{%- include "handle_map.cpp" %} +{%- include "scaffolding/object_map.cpp" %} {%- for typ in ci.iter_types() %} {%- match typ %} @@ -137,13 +142,17 @@ struct RustStream: std::basic_iostream { {%- when Type::Enum { module_path, name } %} {%- let e = ci|get_enum_definition(name) %} {%- if ci.is_name_used_as_error(name) %} +{% for variant in e.variants() %} +{% include "scaffolding/err.hpp" %} +{% endfor %} {%- else %} {%- if e.is_flat() %} {% include "enum_conv.hpp" %} {% endif %} {%- endif %} {%- when Type::Object { module_path, name, imp } %} -HandleMap<{{ typ|canonical_name }}> {{ name }}_map; +{% include "scaffolding/obj.hpp" %} +ObjectMap<{{ typ|canonical_name }}> {{ name }}_map; {%- when Type::CallbackInterface { module_path, name } %} {% include "scaffolding/callback.hpp" %} {% else %} @@ -155,7 +164,7 @@ extern "C" { #endif UNIFFI_EXPORT RustBuffer {{ ci.ffi_rustbuffer_alloc().name() }}(int32_t size, RustCallStatus *out_status) { - out_status->code = 0; + out_status->code = UNIFFI_CALL_STATUS_OK; RustBuffer buf = { .capacity = size, @@ -167,7 +176,7 @@ UNIFFI_EXPORT RustBuffer {{ ci.ffi_rustbuffer_alloc().name() }}(int32_t size, Ru } UNIFFI_EXPORT RustBuffer {{ ci.ffi_rustbuffer_from_bytes().name() }}(ForeignBytes bytes, RustCallStatus *out_status) { - out_status->code = 0; + out_status->code = UNIFFI_CALL_STATUS_OK; RustBuffer buf = { .capacity = bytes.len, @@ -181,13 +190,13 @@ UNIFFI_EXPORT RustBuffer {{ ci.ffi_rustbuffer_from_bytes().name() }}(ForeignByte } UNIFFI_EXPORT void {{ ci.ffi_rustbuffer_free().name() }}(RustBuffer buf, RustCallStatus *out_status) { - out_status->code = 0; + out_status->code = UNIFFI_CALL_STATUS_OK; delete[] buf.data; } UNIFFI_EXPORT RustBuffer {{ ci.ffi_rustbuffer_reserve().name() }}(RustBuffer buffer, int32_t additional, RustCallStatus *out_status) { - out_status->code = 0; + out_status->code = UNIFFI_CALL_STATUS_OK; RustBuffer buf = { .capacity = buffer.capacity + additional, @@ -208,12 +217,9 @@ UNIFFI_EXPORT {% endfor %} {%- if ffi_func.has_rust_call_status_arg() %}RustCallStatus *out_status{% endif -%} ) { - {%- if ffi_func.has_rust_call_status_arg() %} - out_status->code = 0; - {% endif -%} - - {% match func.return_type() %} - {% when Some with (return_type) %} + {%- call macros::fn_prologue(ci, func, ffi_func) %} + {%- match func.return_type() %} + {%- when Some with (return_type) %} auto ret = {{ namespace }}::{{ func.name() }}( {%- for arg in func.arguments() %} {{- arg|lift_fn }}({{ arg.name()|var_name }}){% if !loop.last %}, {% endif -%} @@ -223,15 +229,16 @@ UNIFFI_EXPORT {{ namespace }}::{{ func.name() }}( {%- for arg in func.arguments() %} {{- arg|lift_fn }}({{ arg.name()|var_name }}){% if !loop.last %}, {% endif -%} - {% endfor %}); - {% endmatch %} + {%- endfor %}); + {%- endmatch %} + {%- call macros::fn_epilogue(ci, func, ffi_func) %} } {% endfor %} {% for func in ci.callback_interface_definitions() %} {% let ffi_func = func.ffi_init_callback() %} UNIFFI_EXPORT void {{ ffi_func.name() }}(ForeignCallback callback_stub, RustCallStatus *out_status) { - out_status->code = 0; + out_status->code = UNIFFI_CALL_STATUS_OK; {{ func|ffi_converter_name }}::fn_handle.store(reinterpret_cast(callback_stub)); } @@ -250,14 +257,13 @@ UNIFFI_EXPORT {%- if ffi_ctor.has_rust_call_status_arg() %}RustCallStatus *out_status{% endif -%} ) { {%- if ffi_ctor.has_rust_call_status_arg() %} - out_status->code = 0; + out_status->code = UNIFFI_CALL_STATUS_OK; {% endif -%} std::shared_ptr<{{ obj.name() }}> obj = std::make_shared<{{ obj.name() }}>( {%- for arg in ctor.arguments() %} {{- arg|lift_fn }}({{ arg.name()|var_name }}){% if !loop.last %}, {% endif -%} {% endfor %}); - return (void*){{ obj.name() }}_map.insert(obj); } {% endfor %} @@ -272,7 +278,7 @@ UNIFFI_EXPORT {%- if ffi_dtor.has_rust_call_status_arg() %}RustCallStatus *out_status{% endif -%} ) { {%- if ffi_dtor.has_rust_call_status_arg() %} - out_status->code = 0; + out_status->code = UNIFFI_CALL_STATUS_OK; {% endif -%} {{ obj.name() }}_map.erase((uint64_t)ptr); @@ -289,11 +295,12 @@ UNIFFI_EXPORT {%- if ffi_func.has_rust_call_status_arg() %}RustCallStatus *out_status{% endif -%} ) { {%- if ffi_func.has_rust_call_status_arg() %} - out_status->code = 0; + out_status->code = UNIFFI_CALL_STATUS_OK; {% endif -%} auto obj = {{ obj.name() }}_map.at((uint64_t)ptr); + {%- call macros::fn_prologue(ci, func, ffi_func) %} {% match func.return_type() %} {% when Some with (return_type) %} auto ret = obj->{{ func.name() }}( @@ -307,6 +314,7 @@ UNIFFI_EXPORT {{- arg|lift_fn }}({{ arg.name()|var_name }}){% if !loop.last %}, {% endif -%} {% endfor %}); {% endmatch %} + {%- call macros::fn_epilogue(ci, func, ffi_func) %} } {% endfor %} {% endfor %} @@ -343,7 +351,7 @@ UNIFFI_EXPORT uint32_t {{ contract_fn.name() }}() { return {{ ci.uniffi_contract #endif RustBuffer rustbuffer_alloc(int32_t size) { - RustCallStatus status = { 0 }; + RustCallStatus status = { UNIFFI_CALL_STATUS_OK }; return {{ ci.ffi_rustbuffer_alloc().name() }}( size, @@ -352,7 +360,7 @@ RustBuffer rustbuffer_alloc(int32_t size) { } RustBuffer rustbuffer_from_bytes(const ForeignBytes& bytes) { - RustCallStatus status = { 0 }; + RustCallStatus status = { UNIFFI_CALL_STATUS_OK }; return {{ ci.ffi_rustbuffer_from_bytes().name() }}( bytes, @@ -361,7 +369,7 @@ RustBuffer rustbuffer_from_bytes(const ForeignBytes& bytes) { } void rustbuffer_free(RustBuffer& buf) { - RustCallStatus status = { 0 }; + RustCallStatus status = { UNIFFI_CALL_STATUS_OK }; {{ ci.ffi_rustbuffer_free().name() }}( buf, @@ -417,12 +425,16 @@ void rustbuffer_free(RustBuffer& buf) { {%- when Type::Enum { module_path, name } %} {%- let e = ci|get_enum_definition(name) %} {%- if ci.is_name_used_as_error(name) %} +{% for variant in e.variants() %} +{% include "scaffolding/err.cpp" %} +{% endfor %} {%- else %} {%- if e.is_flat() %} {% include "enum_tmpl.cpp" %} {%- endif %} {%- endif %} {%- when Type::Object { module_path, name, imp } %} +{% include "scaffolding/obj.cpp" %} {%- when Type::CallbackInterface { module_path, name } %} {% include "scaffolding/callback.cpp" %} {%- else %} diff --git a/bindgen/src/bindings/cpp/templates/err_tmpl.cpp b/bindgen/src/bindings/cpp/templates/err_tmpl.cpp index 296d723..3ab0619 100644 --- a/bindgen/src/bindings/cpp/templates/err_tmpl.cpp +++ b/bindgen/src/bindings/cpp/templates/err_tmpl.cpp @@ -83,4 +83,4 @@ int32_t {{ ffi_converter_name }}::allocation_size(const {{ class_name }} &val) { throw std::runtime_error("Unexpected error variant"); } {%- endif %} -} \ No newline at end of file +} diff --git a/bindgen/src/bindings/cpp/templates/opt_tmpl.cpp b/bindgen/src/bindings/cpp/templates/opt_tmpl.cpp index 0107f69..99a22ee 100644 --- a/bindgen/src/bindings/cpp/templates/opt_tmpl.cpp +++ b/bindgen/src/bindings/cpp/templates/opt_tmpl.cpp @@ -60,4 +60,4 @@ int32_t {{ ffi_converter_name }}::allocation_size(const {{ type_name }} &val) { } return ret; -} \ No newline at end of file +} diff --git a/bindgen/src/bindings/cpp/templates/scaffolding/callback.cpp b/bindgen/src/bindings/cpp/templates/scaffolding/callback.cpp index 228ea4a..e221e4d 100644 --- a/bindgen/src/bindings/cpp/templates/scaffolding/callback.cpp +++ b/bindgen/src/bindings/cpp/templates/scaffolding/callback.cpp @@ -43,19 +43,55 @@ class {{ iface.name() }}Proxy: public {{ iface.name() }} { {% endfor %} auto ret = callback_stub(this->handle, {{ loop.index }}, in_buf.data, size, &out_buf); - rustbuffer_free(in_buf); - {% match m.return_type() %} - {% when Some with (return_type) %} - RustStream out_stream(&out_buf); - auto result = {{ return_type|read_fn }}(out_stream); - rustbuffer_free(out_buf); + if (ret == UNIFFI_CALL_STATUS_OK) { + {% match m.return_type() %} + {% when Some with (return_type) %} + RustStream out_stream(&out_buf); + auto result = {{ return_type|read_fn }}(out_stream); + rustbuffer_free(out_buf); + + return result; + {% else %} + rustbuffer_free(out_buf); + {% endmatch %} + } + else if (ret == UNIFFI_CALL_STATUS_ERROR) { + RustStream out_stream(&out_buf); + int32_t v; + out_stream >> v; + {%- if m.throws() %} + switch (v) { + {%- let err_type = m.throws_type().unwrap()|type_name %} + {%- let err_enum = ci.get_enum_definition(err_type).unwrap() %} + {%- for variant in err_enum.variants() %} + {%- let converter_name = err_enum|ffi_converter_name %} + case {{ loop.index }}: + { + auto result = {{ converter_name }}{{ variant.name() }}::read(out_stream, v); + rustbuffer_free(out_buf); + + throw result; + } + {%- endfor %} + default: + rustbuffer_free(out_buf); + throw std::runtime_error("Unexpected error variant: " + std::to_string(v)); + } + {%- endif %} + rustbuffer_free(out_buf); + throw std::runtime_error("Callback returned an error"); + } + else if (ret == UNIFFI_CALL_STATUS_PANIC) { + auto result = FfiConverterString::lift(out_buf); - return result; - {% else %} - rustbuffer_free(out_buf); - {% endmatch %} + throw std::runtime_error(result); + } + else { + rustbuffer_free(out_buf); + throw std::runtime_error("Unknown error code: " + std::to_string(ret)); + } } {% endfor %} private: diff --git a/bindgen/src/bindings/cpp/templates/scaffolding/err.cpp b/bindgen/src/bindings/cpp/templates/scaffolding/err.cpp new file mode 100644 index 0000000..21deecb --- /dev/null +++ b/bindgen/src/bindings/cpp/templates/scaffolding/err.cpp @@ -0,0 +1,63 @@ +{%- let type_name = e|type_name %} +{%- let class_name = type_name|class_name %} +{%- let ffi_converter_name = typ|ffi_converter_name %} +{%- let namespace = type_name|to_lower_snake_case %} + +{{ namespace }}::{{ variant.name() }} {{ ffi_converter_name }}{{ variant.name() }}::lift(RustBuffer buf, int32_t v) { + auto stream = RustStream(&buf); + auto ret = {{ ffi_converter_name }}{{ variant.name() }}::read(stream, v); + + rustbuffer_free(buf); + + return ret; +} + +RustBuffer {{ ffi_converter_name }}{{ variant.name() }}::lower(const {{ namespace }}::{{ variant.name() }} &val) { + auto buf = rustbuffer_alloc(allocation_size(val)); + auto stream = RustStream(&buf); + + {{ ffi_converter_name }}{{ variant.name() }}::write(stream, val); + + return std::move(buf); +} + +{{ namespace }}::{{ variant.name() }} {{ ffi_converter_name }}{{ variant.name() }}::read(RustStream &stream, int32_t v) { + if (v != {{ loop.index }}) { + throw std::runtime_error("Unexpected error variant"); + } + + {%- if e.is_flat() %} + return {{ namespace }}::{{ variant.name() }}(); + {%- else %} + {{ namespace }}::{{ variant.name() }} var; + {%- for field in variant.fields() %} + var.{{ field.name()|var_name }} = {{ field|read_fn }}(stream); + {%- endfor %} + return var; + {%- endif %} +} + +void {{ ffi_converter_name }}{{ variant.name() }}::write(RustStream &stream, const {{ namespace }}::{{ variant.name() }} &val) { + stream << int32_t({{ loop.index }}); + + {%- if e.is_flat() %} + {{ Type::String.borrow()|write_fn }}(stream, val.what()); + {%- else %} + {%- for field in variant.fields() %} + {{ field|write_fn }}(stream, val.{{ field.name()|var_name }}); + {%- endfor %} + {%- endif %} +} + +int32_t {{ ffi_converter_name }}{{ variant.name() }}::allocation_size(const {{ namespace }}::{{ variant.name() }} &val) { + auto size = sizeof(int32_t); + {%- if e.is_flat() %} + size += {{ Type::String.borrow()|allocation_size_fn }}(val.what()); + {%- else %} + {%- for field in variant.fields() %} + size += {{ field|allocation_size_fn }}(val.{{ field.name()|var_name }}); + {%- endfor %} + {%- endif %} + + return size; +} diff --git a/bindgen/src/bindings/cpp/templates/scaffolding/err.hpp b/bindgen/src/bindings/cpp/templates/scaffolding/err.hpp new file mode 100644 index 0000000..46efb2f --- /dev/null +++ b/bindgen/src/bindings/cpp/templates/scaffolding/err.hpp @@ -0,0 +1,11 @@ +{%- let type_name = e|type_name %} +{%- let class_name = type_name|class_name %} +{%- let ffi_converter_name = typ|ffi_converter_name %} +{%- let namespace = type_name|to_lower_snake_case %} +struct {{ ffi_converter_name }}{{ variant.name() }} { + static {{ namespace }}::{{ variant.name() }} lift(RustBuffer buf, int32_t v); + static RustBuffer lower(const {{ namespace }}::{{ variant.name() }} &); + static {{ namespace }}::{{ variant.name() }} read(RustStream &stream, int32_t v); + static void write(RustStream &stream, const {{ namespace }}::{{ variant.name() }} &); + static int32_t allocation_size(const {{ namespace }}::{{ variant.name() }} &); +}; diff --git a/bindgen/src/bindings/cpp/templates/scaffolding/macros.cpp b/bindgen/src/bindings/cpp/templates/scaffolding/macros.cpp new file mode 100644 index 0000000..4b27ee2 --- /dev/null +++ b/bindgen/src/bindings/cpp/templates/scaffolding/macros.cpp @@ -0,0 +1,32 @@ +{% macro fn_prologue(ci, func, ffi_func) -%} +{%- if ffi_func.has_rust_call_status_arg() %} + out_status->code = UNIFFI_CALL_STATUS_OK; +{%- endif %} + try { +{% endmacro %} + +{% macro fn_epilogue(ci, func, ffi_func) -%} +{%- if ffi_func.has_rust_call_status_arg() %} +{%- if func.throws() %} +{%- let err_type = func.throws_type().unwrap()|type_name %} +{%- let err_enum = ci.get_enum_definition(err_type).unwrap() %} +{%- for variant in err_enum.variants() %} +{%- let converter_name = err_enum|ffi_converter_name %} + } catch (const {{ err_type|to_lower_snake_case }}::{{ variant.name() }} &e) { + out_status->code = UNIFFI_CALL_STATUS_ERROR; + out_status->error_buf = {{ converter_name }}{{ variant.name() }}::lower(e); +{%- endfor %} +{%- endif %} + } catch (const std::exception &e) { + out_status->code = UNIFFI_CALL_STATUS_PANIC; + out_status->error_buf = {{ Type::String.borrow()|ffi_converter_name }}::lower(e.what()); + } catch (...) { + out_status->code = UNIFFI_CALL_STATUS_PANIC; + } +{% match func.return_type() %} +{% when Some with (return_type) %} + return {}; +{% else %} +{% endmatch %} +{%- endif %} +{% endmacro %} diff --git a/bindgen/src/bindings/cpp/templates/scaffolding/obj.cpp b/bindgen/src/bindings/cpp/templates/scaffolding/obj.cpp new file mode 100644 index 0000000..240d41b --- /dev/null +++ b/bindgen/src/bindings/cpp/templates/scaffolding/obj.cpp @@ -0,0 +1,24 @@ +{{ type_name }} {{ ffi_converter_name }}::lift(void *ptr) { + return {{ name }}_map.at((uint64_t)ptr); +} + +void *{{ ffi_converter_name }}::lower(const {{ type_name }} &obj) { + auto ret = {{ name }}_map.insert(obj); + return (void *)ret; +} + +{{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { + std::uintptr_t ptr; + stream >> ptr; + + return {{ name }}_map.at(ptr); +} + +void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} &obj) { + {{ name }}_map.insert(obj); + stream << (uint64_t)obj.get(); +} + +int32_t {{ ffi_converter_name }}::allocation_size(const {{ type_name }} &) { + return 8; +} diff --git a/bindgen/src/bindings/cpp/templates/scaffolding/obj.hpp b/bindgen/src/bindings/cpp/templates/scaffolding/obj.hpp new file mode 100644 index 0000000..6b5626d --- /dev/null +++ b/bindgen/src/bindings/cpp/templates/scaffolding/obj.hpp @@ -0,0 +1,8 @@ +{%- let type_name = typ|type_name %} +struct {{ typ|ffi_converter_name }} { + static {{ type_name }} lift(void *); + static void *lower(const {{ type_name }} &); + static {{ type_name }} read(RustStream &); + static void write(RustStream &, const {{ type_name }} &); + static int32_t allocation_size(const {{ type_name }} &); +}; diff --git a/bindgen/src/bindings/cpp/templates/scaffolding/object_map.cpp b/bindgen/src/bindings/cpp/templates/scaffolding/object_map.cpp new file mode 100644 index 0000000..a48ca78 --- /dev/null +++ b/bindgen/src/bindings/cpp/templates/scaffolding/object_map.cpp @@ -0,0 +1,47 @@ +template struct ObjectMap { + ObjectMap() = default; + + std::shared_ptr at(uint64_t ptr) { + std::lock_guard guard(this->mutex); + + return this->map.at(ptr).ptr; + } + + uint64_t insert(std::shared_ptr impl) { + std::lock_guard guard(this->mutex); + auto key = (uint64_t)impl.get(); + + if (this->map.contains(key)) { + this->map.at(key).ref_count += 1; + } else { + this->map.insert({ key, {impl, 1} }); + } + + return key; + } + + void erase(uint64_t ptr) { + std::lock_guard guard(this->mutex); + + auto &wrapper = this->map.at(ptr); + wrapper.ref_count -= 1; + + if (wrapper.ref_count == 0) { + this->map.erase(ptr); + } + } + private: + ObjectMap(const ObjectMap &) = delete; + ObjectMap(ObjectMap &&) = delete; + + ObjectMap &operator=(const ObjectMap &) = delete; + ObjectMap &operator=(ObjectMap &&) = delete; + + struct PtrWrapper { + std::shared_ptr ptr = nullptr; + uint64_t ref_count = 0; + }; + + std::mutex mutex; + std::map map; +}; diff --git a/bindgen/src/bindings/cpp/templates/str_conv.cpp b/bindgen/src/bindings/cpp/templates/str_conv.cpp index 8940d3c..249e33c 100644 --- a/bindgen/src/bindings/cpp/templates/str_conv.cpp +++ b/bindgen/src/bindings/cpp/templates/str_conv.cpp @@ -32,4 +32,4 @@ void {{ ffi_converter_name }}::write(RustStream &stream, const std::string &val) int32_t {{ ffi_converter_name }}::allocation_size(const std::string &val) { return sizeof(int32_t) + val.length(); -} \ No newline at end of file +} diff --git a/cpp-tests/CMakeLists.txt b/cpp-tests/CMakeLists.txt index 14476d2..1ea9293 100644 --- a/cpp-tests/CMakeLists.txt +++ b/cpp-tests/CMakeLists.txt @@ -10,6 +10,17 @@ find_package(Threads REQUIRED) set(BINDINGS_BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../target/debug) set(BINDINGS_SRC_DIR ${BINDINGS_BUILD_DIR}/bindings) +macro(memcheck_test TEST_NAME) +add_test(NAME ${TEST_NAME}-test-memcheck + COMMAND valgrind + --error-exitcode=1 + --tool=memcheck + --leak-check=full + --errors-for-leak-kinds=definite + --show-leak-kinds=definite $) +endmacro(memcheck_test) + +# Add a bindings test case macro(test_case TEST_NAME) add_executable(${TEST_NAME}-test tests/${TEST_NAME}/main.cpp ${BINDINGS_SRC_DIR}/${TEST_NAME}.cpp) @@ -19,14 +30,7 @@ target_link_libraries(${TEST_NAME}-test uniffi_bindgen_cpp_fixtures Threads::Thr target_compile_definitions(${TEST_NAME}-test PRIVATE UNIFFI_BINDING_DIR="${BINDINGS_SRC_DIR}") add_test(NAME ${TEST_NAME}-test COMMAND ${TEST_NAME}-test) - -add_test(NAME ${TEST_NAME}-test-memcheck - COMMAND valgrind - --error-exitcode=1 - --tool=memcheck - --leak-check=full - --errors-for-leak-kinds=definite - --show-leak-kinds=definite $) +memcheck_test(${TEST_NAME}-test) add_dependencies(${TEST_NAME}-test bindings) @@ -34,26 +38,21 @@ list(APPEND BINDING_FILES ${BINDINGS_SRC_DIR}/${TEST_NAME}.cpp) endmacro(test_case) +# Add a scaffolding test case macro(scaffolding_test_case TEST_NAME) add_library(uniffi_${TEST_NAME} SHARED scaffolding_tests/${TEST_NAME}/lib_${TEST_NAME}.cpp) target_include_directories(uniffi_${TEST_NAME} PRIVATE scaffolding_tests/${TEST_NAME} ${BINDINGS_SRC_DIR}) -add_executable(${TEST_NAME}-scaffolding-test scaffolding_tests/${TEST_NAME}/main.cpp ${BINDINGS_SRC_DIR}/${TEST_NAME}.cpp) +add_executable(${TEST_NAME}-scaffolding-test tests/${TEST_NAME}/main.cpp ${BINDINGS_SRC_DIR}/${TEST_NAME}.cpp) target_include_directories(${TEST_NAME}-scaffolding-test PRIVATE ${BINDINGS_SRC_DIR} include) target_link_libraries(${TEST_NAME}-scaffolding-test uniffi_${TEST_NAME} Threads::Threads) +target_compile_definitions(${TEST_NAME}-scaffolding-test PRIVATE SCAFFOLDING_TEST=1) add_test(NAME ${TEST_NAME}-scaffolding-test COMMAND ${TEST_NAME}-scaffolding-test) +memcheck_test(${TEST_NAME}-scaffolding-test) -add_test(NAME ${TEST_NAME}-scaffolding-test-memcheck - COMMAND valgrind - --error-exitcode=1 - --tool=memcheck - --leak-check=full - --errors-for-leak-kinds=definite - --show-leak-kinds=definite $) - -add_dependencies(uniffi_${TEST_NAME} bindings scaffolding custom_scaffolding uniffi_${TEST_NAME}) +add_dependencies(uniffi_${TEST_NAME} bindings scaffolding uniffi_${TEST_NAME}) list(APPEND BINDING_FILES ${BINDINGS_SRC_DIR}/${TEST_NAME}.cpp) list(APPEND SCAFFOLDING_FILES ${BINDINGS_SRC_DIR}/${TEST_NAME}_cpp_scaffolding.cpp) @@ -76,17 +75,16 @@ test_case(trait_methods) test_case(custom_types_builtin) scaffolding_test_case(arithmetic) -# scaffolding_test_case(callbacks) -scaffolding_test_case(custom_fixture_callbacks) +scaffolding_test_case(callbacks) +scaffolding_test_case(fixture_callbacks) scaffolding_test_case(chronological) # scaffolding_test_case(custom_types) scaffolding_test_case(geometry) # scaffolding_test_case(rondpoint) scaffolding_test_case(sprites) -# scaffolding_test_case(todolist) -# scaffolding_test_case(traits) +scaffolding_test_case(todolist) +scaffolding_test_case(traits) # scaffolding_test_case(coverall) -# scaffolding_test_case(trait_methods) # scaffolding_test_case(custom_types_builtin) add_custom_target(libs ALL @@ -110,11 +108,3 @@ add_custom_target(scaffolding ALL WORKING_DIRECTORY ${BINDINGS_BUILD_DIR} COMMENT "Generating scaffolding" ) - -add_custom_target(custom_scaffolding ALL - DEPENDS scaffolding - COMMAND ./uniffi-bindgen-cpp --scaffolding ${CMAKE_CURRENT_SOURCE_DIR}/scaffolding_tests/custom_fixture_callbacks/udl/custom_callback_fixtures.udl --crate fixture_callbacks --out-dir ${BINDINGS_SRC_DIR} - COMMAND ./uniffi-bindgen-cpp ${CMAKE_CURRENT_SOURCE_DIR}/scaffolding_tests/custom_fixture_callbacks/udl/custom_callback_fixtures.udl --crate fixture_callbacks --out-dir ${BINDINGS_SRC_DIR} - WORKING_DIRECTORY ${BINDINGS_BUILD_DIR} - COMMENT "Generating custom test scaffolding" -) diff --git a/cpp-tests/scaffolding_tests/arithmetic/lib_arithmetic.cpp b/cpp-tests/scaffolding_tests/arithmetic/lib_arithmetic.cpp index 44bdf2d..ac65a44 100644 --- a/cpp-tests/scaffolding_tests/arithmetic/lib_arithmetic.cpp +++ b/cpp-tests/scaffolding_tests/arithmetic/lib_arithmetic.cpp @@ -5,10 +5,18 @@ uint64_t arithmetic::add(uint64_t a, uint64_t b) { } uint64_t arithmetic::sub(uint64_t a, uint64_t b) { + if (a < b) { + throw arithmetic_error::IntegerOverflow(); + } + return a - b; } uint64_t arithmetic::div(uint64_t a, uint64_t b) { + if (b == 0) { + throw std::runtime_error("division by zero"); + } + return a / b; } diff --git a/cpp-tests/scaffolding_tests/arithmetic/lib_arithmetic.hpp b/cpp-tests/scaffolding_tests/arithmetic/lib_arithmetic.hpp index 7155f23..7c6abac 100644 --- a/cpp-tests/scaffolding_tests/arithmetic/lib_arithmetic.hpp +++ b/cpp-tests/scaffolding_tests/arithmetic/lib_arithmetic.hpp @@ -1,10 +1,18 @@ #include +#include namespace { namespace arithmetic { + namespace arithmetic_error { + class IntegerOverflow: public std::runtime_error { + public: + IntegerOverflow(): std::runtime_error("Integer overflow") { } + }; + }; + uint64_t add(uint64_t a, uint64_t b); uint64_t sub(uint64_t a, uint64_t b); uint64_t div(uint64_t a, uint64_t b); int8_t equal(uint64_t a, uint64_t b); } -} \ No newline at end of file +} diff --git a/cpp-tests/scaffolding_tests/arithmetic/main.cpp b/cpp-tests/scaffolding_tests/arithmetic/main.cpp deleted file mode 100644 index 3746e74..0000000 --- a/cpp-tests/scaffolding_tests/arithmetic/main.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include - -#include - -int main() { - ASSERT_EQ(arithmetic::add(2, 4), 6ul); - ASSERT_EQ(arithmetic::add(4, 8), 12ul); - - ASSERT_EQ(2ul, arithmetic::sub(4, 2)); - ASSERT_EQ(4ul, arithmetic::sub(8, 4)); - - // EXPECT_EXCEPTION(arithmetic::sub(2, 4), arithmetic::arithmetic_error::IntegerOverflow); - - ASSERT_EQ(4ul, arithmetic::div(8, 2)); - - // EXPECT_EXCEPTION(arithmetic::div(8, 0), std::runtime_error); - - ASSERT_TRUE(arithmetic::equal(2, 2)); - ASSERT_TRUE(arithmetic::equal(4, 4)); - - ASSERT_FALSE(arithmetic::equal(2, 4)); - ASSERT_FALSE(arithmetic::equal(4, 8)); - - - return 0; -} \ No newline at end of file diff --git a/cpp-tests/scaffolding_tests/callbacks/lib_callbacks.cpp b/cpp-tests/scaffolding_tests/callbacks/lib_callbacks.cpp new file mode 100644 index 0000000..dc0f8bb --- /dev/null +++ b/cpp-tests/scaffolding_tests/callbacks/lib_callbacks.cpp @@ -0,0 +1,13 @@ +#include "lib_callbacks.hpp" + +std::string callbacks::Telephone::call(std::shared_ptr answerer) { + try { + return answerer->answer(); + } catch (telephone_error::Busy& e) { + throw e; + } catch (std::runtime_error& e) { + throw telephone_error::InternalTelephoneError(); + } +} + +#include diff --git a/cpp-tests/scaffolding_tests/callbacks/lib_callbacks.hpp b/cpp-tests/scaffolding_tests/callbacks/lib_callbacks.hpp new file mode 100644 index 0000000..3e2cdc3 --- /dev/null +++ b/cpp-tests/scaffolding_tests/callbacks/lib_callbacks.hpp @@ -0,0 +1,33 @@ +#include +#include +#include + +namespace { + namespace callbacks { + namespace telephone_error { + class Busy : public std::runtime_error { + public: + Busy() : std::runtime_error("I'm busy") {} + }; + + class InternalTelephoneError : public std::runtime_error { + public: + InternalTelephoneError() : std::runtime_error("Internal telephone error") {} + }; + } + + class CallAnswerer { + public: + virtual std::string answer() = 0; + + virtual ~CallAnswerer() = default; + }; + + class Telephone { + public: + Telephone() = default; + + std::string call(std::shared_ptr answerer); + }; + } +} diff --git a/cpp-tests/scaffolding_tests/chronological/lib_chronological.cpp b/cpp-tests/scaffolding_tests/chronological/lib_chronological.cpp index 81e6b6c..489ed7e 100644 --- a/cpp-tests/scaffolding_tests/chronological/lib_chronological.cpp +++ b/cpp-tests/scaffolding_tests/chronological/lib_chronological.cpp @@ -16,7 +16,7 @@ std::string chronological::to_string_timestamp(chronological::timestamp a) { std::time_t time = std::chrono::system_clock::to_time_t(a); std::tm tm = *std::gmtime(&time); std::stringstream ss; - ss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S.000000000Z"); + ss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S"); return ss.str(); } @@ -29,6 +29,9 @@ chronological::timestamp chronological::add(chronological::timestamp a, chronolo } chronological::duration chronological::diff(chronological::timestamp a, chronological::timestamp b) { + if (a < b) { + throw chronological_error::TimeDiffError(); + } return a - b; } diff --git a/cpp-tests/scaffolding_tests/chronological/lib_chronological.hpp b/cpp-tests/scaffolding_tests/chronological/lib_chronological.hpp index f7f029f..db30bf7 100644 --- a/cpp-tests/scaffolding_tests/chronological/lib_chronological.hpp +++ b/cpp-tests/scaffolding_tests/chronological/lib_chronological.hpp @@ -1,32 +1,35 @@ #include #include #include +#include namespace { namespace chronological { typedef std::chrono::time_point timestamp; typedef std::chrono::duration duration; - timestamp return_timestamp(timestamp a); + namespace chronological_error { + class TimeOverflow: public std::runtime_error { + public: + TimeOverflow(): std::runtime_error("Time overflow") { } + }; - duration return_duration(duration a); + class TimeDiffError: public std::runtime_error { + public: + TimeDiffError(): std::runtime_error("Time Diff error") { } + }; + }; + timestamp return_timestamp(timestamp a); + duration return_duration(duration a); std::string to_string_timestamp(timestamp a); - timestamp get_pre_epoch_timestamp(); - timestamp add(timestamp a, duration b); - duration diff(timestamp a, timestamp b); - timestamp now(); - bool equal(timestamp a, timestamp b); - bool optional(std::optional a, std::optional b); - uint64_t get_seconds_before_unix_epoch(timestamp a); - timestamp set_seconds_before_unix_epoch(uint64_t seconds); } } diff --git a/cpp-tests/scaffolding_tests/chronological/main.cpp b/cpp-tests/scaffolding_tests/chronological/main.cpp deleted file mode 100644 index adc5b45..0000000 --- a/cpp-tests/scaffolding_tests/chronological/main.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -using namespace std::chrono_literals; - -std::chrono::time_point epoch_second(int seconds, int nanoseconds) { - return std::chrono::time_point(std::chrono::seconds(seconds)) + std::chrono::nanoseconds(nanoseconds); -} - - -std::chrono::duration time_span_seconds(int seconds, int nanoseconds) { - return std::chrono::nanoseconds(std::chrono::seconds(seconds) + std::chrono::nanoseconds(nanoseconds)); - -} - -std::chrono::time_point time_from_string(const std::string& time) { - std::tm tm = {}; - auto ss = std::stringstream(time); - ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S"); - - return std::chrono::system_clock::from_time_t(timegm(&tm)); -} - -int main() { - ASSERT_EQ( - epoch_second(101, 110), - chronological::add(epoch_second(100, 100), time_span_seconds(1, 10)) - ); - - ASSERT_EQ( - time_span_seconds(1, 100), - chronological::diff(epoch_second(101, 200), epoch_second(100, 100)) - ); - - // EXPECT_EXCEPTION( - // chronological::diff(epoch_second(100, 0), epoch_second(101, 0)), - // chronological::ChronologicalError - // ); - - ASSERT_EQ( - std::chrono::time_point::min(), - chronological::return_timestamp(std::chrono::time_point::min()) - ); - - ASSERT_EQ( - std::chrono::time_point::max(), - chronological::return_timestamp(std::chrono::time_point::max()) - ); - - ASSERT_EQ( - std::chrono::nanoseconds::max(), - chronological::return_duration(std::chrono::nanoseconds::max()) - ); - - // { - // auto time_str = "1969-12-12T00:00:00.000000000Z"; - // auto time = time_from_string(time_str); - - // ASSERT_EQ(time_str, chronological::to_string_timestamp(time)); - // } - - // { - // // get_time doesn't support nanoseconds, so we have to add them manually - // auto time_str = "1969-12-31T23:59:58.999999900Z"; - // auto time = time_from_string(time_str) + 999999900ns; - - // ASSERT_EQ(time_str, chronological::to_string_timestamp(time)); - // } - - // { - // // get_time doesn't support nanoseconds, so we have to add them manually - // auto time = time_from_string("1955-11-05T00:06:01.283000200Z") + 283000200ns; - // auto time2 = time_from_string("1955-11-05T00:06:00.283000100Z") + 283000100ns; - - // ASSERT_EQ( - // time, - // chronological::add(time2, time_span_seconds(1, 100)) - // ); - // } - - auto before = std::chrono::system_clock::now(); - std::this_thread::sleep_for(1s); - auto after = chronological::now(); - std::this_thread::sleep_for(1s); - auto after2 = std::chrono::system_clock::now(); - ASSERT_EQ(-1, std::chrono::duration_cast(before - after).count()); - ASSERT_EQ(1, std::chrono::duration_cast(after2 - after).count()); - - ASSERT_TRUE(chronological::optional(std::chrono::time_point::max(), time_span_seconds(0, 0))); - ASSERT_FALSE(chronological::optional(std::nullopt, time_span_seconds(0, 0))); - ASSERT_FALSE(chronological::optional(std::chrono::time_point::min(), std::nullopt)); - - return 0; -} diff --git a/cpp-tests/scaffolding_tests/custom_fixture_callbacks/Cargo.toml b/cpp-tests/scaffolding_tests/custom_fixture_callbacks/Cargo.toml deleted file mode 100644 index 21830ac..0000000 --- a/cpp-tests/scaffolding_tests/custom_fixture_callbacks/Cargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[package] -name = "custom-fixture-callbacks" diff --git a/cpp-tests/scaffolding_tests/custom_fixture_callbacks/lib_custom_fixture_callbacks.cpp b/cpp-tests/scaffolding_tests/custom_fixture_callbacks/lib_custom_fixture_callbacks.cpp deleted file mode 100644 index 5e19967..0000000 --- a/cpp-tests/scaffolding_tests/custom_fixture_callbacks/lib_custom_fixture_callbacks.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "lib_custom_fixture_callbacks.hpp" -#include - -bool custom_fixture_callbacks::NativeGetters::get_bool(std::shared_ptr cb, bool v, bool arg2) { - return cb->get_bool(v, arg2); -} - -std::string custom_fixture_callbacks::NativeGetters::get_string(std::shared_ptr cb, std::string v, bool arg2) { - return cb->get_string(v, arg2); -} - -std::optional custom_fixture_callbacks::NativeGetters::get_option(std::shared_ptr cb, std::optional v, bool arg2) { - return cb->get_option(v, arg2); -} - -std::vector custom_fixture_callbacks::NativeGetters::get_list(std::shared_ptr cb, std::vector v, bool arg2) { - return cb->get_list(v, arg2); -} - -std::vector custom_fixture_callbacks::NativeGetters::get_bytes(std::shared_ptr cb, std::vector v, bool arg2) { - return cb->get_bytes(v, arg2); -} - -std::optional custom_fixture_callbacks::NativeGetters::get_string_optional_callback(std::shared_ptr cb, std::string v, bool arg2) { - if (cb) { - return cb->get_option(v, arg2); - } else { - return std::nullopt; - } -} - -void custom_fixture_callbacks::NativeGetters::get_nothing(std::shared_ptr cb, std::string v) { - cb->get_nothing(v); -} - -custom_fixture_callbacks::Enumeration custom_fixture_callbacks::NativeGetters::get_enum(std::shared_ptr cb, custom_fixture_callbacks::Enumeration v, uint32_t variant, bool arg2) { - return cb->get_enum(v, variant, arg2); -} - -std::string custom_fixture_callbacks::NativeStringifier::from_simple_type(int32_t value) { - return cb->from_simple_type(value); -} - -std::string custom_fixture_callbacks::NativeStringifier::from_complex_type(std::optional>> values) { - return cb->from_complex_type(values); -} - -#include diff --git a/cpp-tests/scaffolding_tests/custom_fixture_callbacks/lib_custom_fixture_callbacks.hpp b/cpp-tests/scaffolding_tests/custom_fixture_callbacks/lib_custom_fixture_callbacks.hpp deleted file mode 100644 index e62e1c7..0000000 --- a/cpp-tests/scaffolding_tests/custom_fixture_callbacks/lib_custom_fixture_callbacks.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace { - namespace custom_fixture_callbacks { - typedef std::optional>> ComplexType; - - enum class Enumeration { - A = 1, - B, - C, - UNKNOWN, - }; - - class ForeignGetters { - public: - virtual bool get_bool(bool v, bool arg2) = 0; - virtual std::string get_string(std::string v, bool arg2) = 0; - virtual std::optional get_option(std::optional v, bool arg2) = 0; - virtual std::vector get_list(std::vector v, bool arg2) = 0; - virtual std::vector get_bytes(std::vector v, bool arg2) = 0; - virtual void get_nothing(std::string v) = 0; - virtual Enumeration get_enum(Enumeration v, uint32_t variant, bool arg2) = 0; - - virtual ~ForeignGetters() = default; - }; - - class StoredForeignStringifier { - public: - virtual std::string from_simple_type(int32_t value) = 0; - virtual std::string from_complex_type(std::optional>> values) = 0; - - virtual ~StoredForeignStringifier() = default; - }; - - class NativeGetters { - public: - bool get_bool(std::shared_ptr cb, bool v, bool arg2); - std::string get_string(std::shared_ptr cb, std::string v, bool arg2); - std::optional get_option(std::shared_ptr cb, std::optional v, bool arg2); - std::vector get_list(std::shared_ptr cb, std::vector v, bool arg2); - std::vector get_bytes(std::shared_ptr cb, std::vector v, bool arg2); - std::optional get_string_optional_callback(std::shared_ptr cb, std::string v, bool arg2); - void get_nothing(std::shared_ptr cb, std::string v); - Enumeration get_enum(std::shared_ptr cb, Enumeration v, uint32_t variant, bool arg2); - }; - - class NativeStringifier { - public: - NativeStringifier(std::shared_ptr cb) : cb(cb) {} - - std::string from_simple_type(int32_t value); - std::string from_complex_type(std::optional>> values); - private: - std::shared_ptr cb; - }; - } // namespace custom_fixture_callbacks -} // namespace diff --git a/cpp-tests/scaffolding_tests/custom_fixture_callbacks/main.cpp b/cpp-tests/scaffolding_tests/custom_fixture_callbacks/main.cpp deleted file mode 100644 index 5eda176..0000000 --- a/cpp-tests/scaffolding_tests/custom_fixture_callbacks/main.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include - -#include - -#include -#include - -struct ForeignGetters : public custom_fixture_callbacks::ForeignGetters { -public: - virtual bool get_bool(bool v, bool arg2) override { - return v ^ arg2; - } - - virtual std::string get_string(std::string v, bool arg2) override { - return arg2 ? "1234567890123" : v; - } - - virtual std::vector get_list(std::vector v, bool arg2) override { - return arg2 ? v : std::vector(); - } - - virtual std::optional get_option(std::optional v, bool arg2) override { - if (v.has_value()) { - - if (arg2) { - std::transform(v.value().begin(), v.value().end(), v.value().begin(), [](char c) { return ::toupper(c); }); - } - - return v; - } - - return std::nullopt; - } - - virtual std::vector get_bytes(std::vector v, bool arg2) override { - return arg2 ? v : std::vector(); - } - - virtual void get_nothing(std::string v) override { - // TODO errors - } - - virtual custom_fixture_callbacks::Enumeration get_enum(custom_fixture_callbacks::Enumeration v, uint32_t variant, bool arg2) override { - if (!arg2) { - return v; - } - - switch (variant) { - case 0: - return custom_fixture_callbacks::Enumeration::A; - case 1: - return custom_fixture_callbacks::Enumeration::B; - case 2: - return custom_fixture_callbacks::Enumeration::C; - default: - return custom_fixture_callbacks::Enumeration::UNKNOWN; - } - } -}; - -struct CppStringifier : public custom_fixture_callbacks::StoredForeignStringifier { - std::string from_simple_type(int32_t value) override { return "C++: " + std::to_string(value); } - - std::string from_complex_type(std::optional>> values) override { - if (values.has_value()) { - std::string result = "C++:"; - for (auto &v : values.value()) { - if (v.has_value()) { - result += std::to_string(v.value()); - } else { - result += "null"; - } - result += ","; - } - - if (values.value().size() > 0) { - result.pop_back(); - } - - return result; - } else { - return "C++: null"; - } - } -}; - -int main() { - auto cb = std::make_shared(); - auto native_cb = custom_fixture_callbacks::NativeGetters::init(); - - auto flag = true; - - ASSERT_EQ(cb->get_string("test", flag), native_cb->get_string(cb, "test", flag)); - ASSERT_EQ(cb->get_bool(true, flag), native_cb->get_bool(cb, true, flag)); - ASSERT_EQ(cb->get_bool(false, flag), native_cb->get_bool(cb, false, flag)); - - for (auto list : {std::vector{1, 2}, std::vector{0, 1}}) { - ASSERT_EQ(cb->get_list(list, flag), native_cb->get_list(cb, list, flag)); - } - - for (auto str : {"Hello", "World"}) { - ASSERT_EQ(cb->get_string(str, flag), native_cb->get_string(cb, str, flag)); - } - - for (auto opt : {std::optional("Hello"), std::optional(std::nullopt)}) { - ASSERT_EQ(cb->get_option(opt, flag), native_cb->get_option(cb, opt, flag)); - } - - ASSERT_EQ("TestString", native_cb->get_string_optional_callback(cb, "TestString", false)); - ASSERT_EQ(native_cb->get_string_optional_callback(nullptr, "TestString", false), std::nullopt); - - for (auto bytes : {std::vector{1, 2}, std::vector{0, 1}}) { - ASSERT_EQ(cb->get_bytes(bytes, flag), native_cb->get_bytes(cb, bytes, flag)); - } - - for (auto enum_val : {custom_fixture_callbacks::Enumeration::A, custom_fixture_callbacks::Enumeration::B, custom_fixture_callbacks::Enumeration::C}) { - for (auto variant : {0, 1, 2}) { - ASSERT_EQ(cb->get_enum(enum_val, variant, flag), native_cb->get_enum(cb, enum_val, variant, flag)); - } - } - - ASSERT_EQ(native_cb->get_enum(cb, custom_fixture_callbacks::Enumeration::B, 0, false), custom_fixture_callbacks::Enumeration::B); - ASSERT_EQ(native_cb->get_enum(cb, custom_fixture_callbacks::Enumeration::B, 10, true), custom_fixture_callbacks::Enumeration::UNKNOWN); - - auto stringifier = std::make_shared(); - auto native_stringifier = custom_fixture_callbacks::NativeStringifier::init(stringifier); - - for (auto num : {1, 2}) { - ASSERT_EQ(stringifier->from_simple_type(num), native_stringifier->from_simple_type(num)); - } - - for (auto num : { std::optional>>(), std::optional>>(std::vector>{std::nullopt, 1.0, 2.0}) }) { - ASSERT_EQ(stringifier->from_complex_type(num), native_stringifier->from_complex_type(num)); - } - - return 0; -} diff --git a/cpp-tests/scaffolding_tests/custom_fixture_callbacks/udl/custom_callback_fixtures.udl b/cpp-tests/scaffolding_tests/custom_fixture_callbacks/udl/custom_callback_fixtures.udl deleted file mode 100644 index 0451367..0000000 --- a/cpp-tests/scaffolding_tests/custom_fixture_callbacks/udl/custom_callback_fixtures.udl +++ /dev/null @@ -1,42 +0,0 @@ -namespace custom_fixture_callbacks {}; - -enum Enumeration { - "A", - "B", - "C", - "UNKNOWN", -}; - -callback interface ForeignGetters { - boolean get_bool(boolean v, boolean arg2); - string get_string(string v, boolean arg2); - string? get_option(string? v, boolean arg2); - sequence get_list(sequence v, boolean arg2); - bytes get_bytes(bytes v, boolean arg2); - void get_nothing(string v); - Enumeration get_enum(Enumeration v, u32 variant, boolean arg2); -}; - -interface NativeGetters { - constructor(); - - boolean get_bool(ForeignGetters callback, boolean v, boolean arg2); - string get_string(ForeignGetters callback, string v, boolean arg2); - string? get_option(ForeignGetters callback, string? v, boolean arg2); - sequence get_list(ForeignGetters callback, sequence v, boolean arg2); - bytes get_bytes(ForeignGetters callback, bytes v, boolean arg2); - string? get_string_optional_callback(ForeignGetters? callback, string v, boolean arg2); - void get_nothing(ForeignGetters callback, string v); - Enumeration get_enum(ForeignGetters callback, Enumeration v, u32 variant, boolean arg2); -}; - -callback interface StoredForeignStringifier { - string from_simple_type(i32 value); - string from_complex_type(sequence? values); -}; - -interface NativeStringifier { - constructor(StoredForeignStringifier callback); - string from_simple_type(i32 value); - string from_complex_type(sequence? values); -}; diff --git a/cpp-tests/scaffolding_tests/fixture_callbacks/lib_fixture_callbacks.cpp b/cpp-tests/scaffolding_tests/fixture_callbacks/lib_fixture_callbacks.cpp new file mode 100644 index 0000000..e221a53 --- /dev/null +++ b/cpp-tests/scaffolding_tests/fixture_callbacks/lib_fixture_callbacks.cpp @@ -0,0 +1,56 @@ +#include "lib_fixture_callbacks.hpp" + +bool fixture_callbacks::RustGetters::get_bool(std::shared_ptr foreign, bool v, bool argument_two) { + return foreign->get_bool(v, argument_two); +} + +std::string fixture_callbacks::RustGetters::get_string(std::shared_ptr foreign, std::string v, bool argument_two) { + try { + return foreign->get_string(v, argument_two); + } catch (const std::runtime_error &e) { + throw fixture_callbacks::simple_error::UnexpectedError(e.what()); + } +} + +std::optional fixture_callbacks::RustGetters::get_option(std::shared_ptr foreign, std::optional v, bool argument_two) { + try { + return foreign->get_option(v, argument_two); + } catch (const fixture_callbacks::complex_error::ReallyBadArgument &e) { + throw e; + } catch (const std::runtime_error &e) { + throw fixture_callbacks::complex_error::UnexpectedErrorWithReason(e.what()); + } +} + +std::vector fixture_callbacks::RustGetters::get_list(std::shared_ptr foreign, std::vector v, bool argument_two) { + return foreign->get_list(v, argument_two); +} + +std::optional fixture_callbacks::RustGetters::get_string_optional_callback(std::shared_ptr foreign, std::string v, bool argument_two) { + if (foreign) { + return foreign->get_string(v, argument_two); + } + + return std::nullopt; +} + +void fixture_callbacks::RustGetters::get_nothing(std::shared_ptr foreign, std::string v) { + try { + return foreign->get_nothing(v); + } catch (const fixture_callbacks::simple_error::BadArgument &e) { + throw e; + } catch (const std::runtime_error &e) { + throw fixture_callbacks::simple_error::UnexpectedError(e.what()); + } +} + +std::string fixture_callbacks::RustStringifier::from_simple_type(uint32_t v) { + return this->foreign->from_simple_type(v); +} + +std::string fixture_callbacks::RustStringifier::from_complex_type(std::optional>> values) { + return this->foreign->from_complex_type(values); +} + + +#include diff --git a/cpp-tests/scaffolding_tests/fixture_callbacks/lib_fixture_callbacks.hpp b/cpp-tests/scaffolding_tests/fixture_callbacks/lib_fixture_callbacks.hpp new file mode 100644 index 0000000..ea53dd0 --- /dev/null +++ b/cpp-tests/scaffolding_tests/fixture_callbacks/lib_fixture_callbacks.hpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include + +namespace { + namespace fixture_callbacks { + class SimpleError: public std::runtime_error { + public: + SimpleError() : std::runtime_error("") {} + SimpleError(const std::string &what_arg) : std::runtime_error(what_arg) {} + }; + + namespace simple_error { + class BadArgument : public SimpleError { + public: + BadArgument() : SimpleError("") {} + BadArgument(const std::string &what_arg) : SimpleError(what_arg) {} + }; + + class UnexpectedError : public SimpleError { + public: + UnexpectedError() : SimpleError("") {} + UnexpectedError(const std::string &what_arg) : SimpleError(what_arg) {} + }; + } + + class ComplexError: public std::runtime_error { + public: + ComplexError() : std::runtime_error("") {} + ComplexError(const std::string &what_arg) : std::runtime_error(what_arg) {} + }; + + namespace complex_error { + class ReallyBadArgument : public ComplexError { + public: + ReallyBadArgument() : ComplexError("") {} + ReallyBadArgument(const std::string &what_arg) : ComplexError(what_arg) {} + + uint32_t code; + }; + + class UnexpectedErrorWithReason : public ComplexError { + public: + UnexpectedErrorWithReason() : ComplexError("") {} + UnexpectedErrorWithReason(const std::string &what_arg) : ComplexError(what_arg), reason(what_arg) {} + + std::string reason; + }; + } + + class ForeignGetters { + public: + virtual ~ForeignGetters() = default; + + virtual bool get_bool(bool v, bool argument_two) = 0; + virtual std::string get_string(std::string v, bool argument_two) = 0; + virtual std::optional get_option(std::optional v, bool argument_two) = 0; + virtual std::vector get_list(std::vector v, bool argument_two) = 0; + virtual void get_nothing(std::string v) = 0; + }; + + class RustGetters { + public: + RustGetters() = default; + + bool get_bool(std::shared_ptr foreign, bool v, bool argument_two); + std::string get_string(std::shared_ptr foreign, std::string v, bool argument_two); + std::optional get_option(std::shared_ptr foreign, std::optional v, bool argument_two); + std::vector get_list(std::shared_ptr foreign, std::vector v, bool argument_two); + std::optional get_string_optional_callback(std::shared_ptr callback, std::string v, bool argument_two); + void get_nothing(std::shared_ptr foreign, std::string v); + }; + + class StoredForeignStringifier { + public: + virtual ~StoredForeignStringifier() = default; + + virtual std::string from_simple_type(int32_t v) = 0; + virtual std::string from_complex_type(std::optional>> values) = 0; + }; + + class RustStringifier { + public: + RustStringifier(std::shared_ptr foreign) : foreign(foreign) {} + + std::string from_simple_type(uint32_t v); + std::string from_complex_type(std::optional>> values); + private: + std::shared_ptr foreign; + }; + } +} diff --git a/cpp-tests/scaffolding_tests/geometry/main.cpp b/cpp-tests/scaffolding_tests/geometry/main.cpp deleted file mode 100644 index 26570f4..0000000 --- a/cpp-tests/scaffolding_tests/geometry/main.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include - -#include - -int main() { - auto line1 = geometry::Line { - geometry::Point { 0.0, 0.0 }, - geometry::Point { 1.0, 2.0 } - }; - - auto line2 = geometry::Line { - geometry::Point { 1.0, 1.0 }, - geometry::Point { 2.0, 2.0 } - }; - - ASSERT_EQ(geometry::gradient(line1), 2.0); - ASSERT_EQ(geometry::gradient(line2), 1.0); - - auto point = geometry::Point { 0.0, 0.0 }; - auto intersection = geometry::intersection(line1, line2); - ASSERT_TRUE(intersection.has_value()); - auto intersection_value = intersection.value(); - ASSERT_EQ(intersection_value.coord_x, 0); - ASSERT_EQ(intersection_value.coord_y, 0); - - ASSERT_EQ(std::nullopt, geometry::intersection(line1, line1)); - - return 0; -} \ No newline at end of file diff --git a/cpp-tests/scaffolding_tests/sprites/main.cpp b/cpp-tests/scaffolding_tests/sprites/main.cpp deleted file mode 100644 index 20454ec..0000000 --- a/cpp-tests/scaffolding_tests/sprites/main.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include - -#include - -int main() { - auto sprite = sprites::Sprite::init(std::nullopt); - ASSERT_EQ(sprite->get_position().x, 0.0); - ASSERT_EQ(sprite->get_position().y, 0.0); - - auto s = sprites::Sprite::init(sprites::Point { 1.0, 2.0 }); - ASSERT_EQ(s->get_position().x, 1.0); - ASSERT_EQ(s->get_position().y, 2.0); - - s->move_to(sprites::Point { 3.0, 4.0 }); - ASSERT_EQ(s->get_position().x, 3.0); - ASSERT_EQ(s->get_position().y, 4.0); - - s->move_by(sprites::Vector { -4.0, 2.0 }); - ASSERT_EQ(s->get_position().x, -1.0); - ASSERT_EQ(s->get_position().y, 6.0); - - auto rel = sprites::Sprite::new_relative_to(sprites::Point {0.0, 1.0}, sprites::Vector { 1.0, 1.5}); - ASSERT_EQ(rel->get_position().x, 1.0); - ASSERT_EQ(rel->get_position().y, 2.5); - - return 0; -} diff --git a/cpp-tests/scaffolding_tests/todolist/lib_todolist.cpp b/cpp-tests/scaffolding_tests/todolist/lib_todolist.cpp new file mode 100644 index 0000000..925f0ff --- /dev/null +++ b/cpp-tests/scaffolding_tests/todolist/lib_todolist.cpp @@ -0,0 +1,102 @@ +#include "lib_todolist.hpp" + +#include + +std::shared_ptr todolist::get_default_list() { + std::lock_guard lock(todolist::default_list_mutex); + return todolist::default_list; +} + +void todolist::set_default_list(std::shared_ptr list) { + std::lock_guard lock(todolist::default_list_mutex); + todolist::default_list = list; +} + +todolist::TodoEntry todolist::create_entry_with(const std::string &todo) { + if (todo.empty()) { + throw todolist::todo_error::EmptyString("Cannot add empty string as entry"); + } + + return todolist::TodoEntry(todo); +} + +void todolist::TodoList::add_item(const std::string &todo) { + if (todo.empty()) { + throw todolist::todo_error::EmptyString("Cannot add empty string as item"); + } + + std::lock_guard lock(this->items_mutex); + if (std::find(this->items.begin(), this->items.end(), todo) != this->items.end()) { + throw todolist::todo_error::DuplicateTodo("Item already exists"); + } + + this->items.push_back(todo); +} + +void todolist::TodoList::add_entry(const todolist::TodoEntry &entry) { + this->add_item(entry.text); +} + +std::vector todolist::TodoList::get_entries() { + std::lock_guard lock(this->items_mutex); + std::vector entries; + for (const auto &item : this->items) { + entries.push_back(todolist::TodoEntry(item)); + } + return entries; +} + +std::vector todolist::TodoList::get_items() { + std::lock_guard lock(this->items_mutex); + return this->items; +} + +void todolist::TodoList::add_entries(const std::vector &entries) { + for (const auto &entry : entries) { + this->add_entry(entry); + } +} + +void todolist::TodoList::add_items(const std::vector &items) { + for (const auto &item : items) { + this->add_item(item); + } +} + +todolist::TodoEntry todolist::TodoList::get_last_entry() { + std::lock_guard lock(this->items_mutex); + if (this->items.empty()) { + throw todolist::todo_error::EmptyTodoList("List is empty"); + } + + return todolist::TodoEntry(this->items.back()); +} + +std::string todolist::TodoList::get_last() { + return this->get_last_entry().text; +} + +std::string todolist::TodoList::get_first() { + std::lock_guard lock(this->items_mutex); + if (this->items.empty()) { + throw todolist::todo_error::EmptyTodoList("List is empty"); + } + + return this->items.front(); +} + +void todolist::TodoList::clear_item(const std::string &todo) { + std::lock_guard lock(this->items_mutex); + auto it = std::find(this->items.begin(), this->items.end(), todo); + if (it == this->items.end()) { + throw todolist::todo_error::TodoDoesNotExist("Item not found"); + } + + this->items.erase(it); +} + +void todolist::TodoList::make_default() { + todolist::set_default_list(this->shared_from_this()); +} + +#include diff --git a/cpp-tests/scaffolding_tests/todolist/lib_todolist.hpp b/cpp-tests/scaffolding_tests/todolist/lib_todolist.hpp new file mode 100644 index 0000000..c7e0e7a --- /dev/null +++ b/cpp-tests/scaffolding_tests/todolist/lib_todolist.hpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include + +namespace { + namespace todolist { + class TodoError: public std::runtime_error { + public: + TodoError() : std::runtime_error("") {} + TodoError(const std::string &what_arg) : std::runtime_error(what_arg) {} + }; + + namespace todo_error { + class TodoDoesNotExist: public TodoError { + public: + TodoDoesNotExist() : TodoError("") {} + TodoDoesNotExist(const std::string &what_arg) : TodoError(what_arg) {} + }; + + class EmptyTodoList: public TodoError { + public: + EmptyTodoList() : TodoError("") {} + EmptyTodoList(const std::string &what_arg) : TodoError(what_arg) {} + }; + + class DuplicateTodo: public TodoError { + public: + DuplicateTodo() : TodoError("") {} + DuplicateTodo(const std::string &what_arg) : TodoError(what_arg) {} + }; + + class EmptyString: public TodoError { + public: + EmptyString() : TodoError("") {} + EmptyString(const std::string &what_arg) : TodoError(what_arg) {} + }; + + class DeligatedError: public TodoError { + public: + DeligatedError() : TodoError("") {} + DeligatedError(const std::string &what_arg) : TodoError(what_arg) {} + }; + } + + struct TodoEntry { + std::string text; + }; + + class TodoList: public std::enable_shared_from_this{ + public: + TodoList() = default; + + void add_item(const std::string &item); + void add_entry(const TodoEntry &entry); + std::vector get_entries(); + std::vector get_items(); + void add_entries(const std::vector &entries); + void add_items(const std::vector &items); + TodoEntry get_last_entry(); + std::string get_last(); + std::string get_first(); + void clear_item(const std::string &item); + void make_default(); + private: + std::vector items; + std::mutex items_mutex; + }; + + std::shared_ptr get_default_list(); + void set_default_list(std::shared_ptr list); + + TodoEntry create_entry_with(const std::string &text); + + static std::shared_ptr default_list = nullptr; + static std::mutex default_list_mutex; + } +} diff --git a/cpp-tests/scaffolding_tests/traits/lib_traits.cpp b/cpp-tests/scaffolding_tests/traits/lib_traits.cpp new file mode 100644 index 0000000..7e1bd61 --- /dev/null +++ b/cpp-tests/scaffolding_tests/traits/lib_traits.cpp @@ -0,0 +1,3 @@ +#include "lib_traits.hpp" + +#include diff --git a/cpp-tests/scaffolding_tests/traits/lib_traits.hpp b/cpp-tests/scaffolding_tests/traits/lib_traits.hpp new file mode 100644 index 0000000..270d3fa --- /dev/null +++ b/cpp-tests/scaffolding_tests/traits/lib_traits.hpp @@ -0,0 +1,35 @@ +#include +#include +#include + +namespace { + namespace traits { + class Button { + public: + virtual ~Button() = default; + virtual std::string name() = 0; + }; + + class GoButton: public Button { + public: + std::string name() override { + return "go"; + } + }; + + class StopButton: public Button { + public: + std::string name() override { + return "stop"; + } + }; + + std::vector> get_buttons() { + return {std::make_shared(), std::make_shared()}; + } + + std::shared_ptr