├── .cargo └── config.toml ├── .clang-format-ignore ├── .github ├── actions │ └── test-turbo-modules │ │ └── action.yml └── workflows │ ├── ci.yml │ ├── cocoapods.yml │ ├── compat-android-latest.yml │ ├── compat-android.yml │ ├── compat-ios-latest.yml │ ├── compat-ios.yml │ ├── crates-io.yaml │ ├── mdbook.yml │ └── npm.yml ├── .gitignore ├── .licence-config.yaml ├── .npmignore ├── .prettierignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Cargo.toml ├── LICENSE ├── README.md ├── bin └── cli.cjs ├── cpp ├── hermes-extension │ └── CMakeLists.txt ├── hermes-rust-extension │ └── CMakeLists.txt ├── includes │ ├── Bridging.h │ ├── Float32.h │ ├── Float64.h │ ├── ForeignBytes.h │ ├── Int16.h │ ├── Int32.h │ ├── Int64.h │ ├── Int8.h │ ├── ReferenceHolder.h │ ├── RustArcPtr.h │ ├── RustBuffer.h │ ├── Uint16.h │ ├── Uint32.h │ ├── Uint64.h │ ├── Uint8.h │ ├── UniffiByteArray.h │ ├── UniffiCallInvoker.h │ ├── UniffiJsiTypes.h │ ├── UniffiRustCallStatus.h │ ├── UniffiString.h │ └── registerNatives.h ├── stubs │ └── ReactCommon │ │ └── CallInvoker.h └── test-harness │ ├── CMakeLists.txt │ ├── MyCallInvoker.h │ ├── README.md │ ├── test-runner.cpp │ └── timers.js.inc ├── crates ├── ubrn_bindgen │ ├── Cargo.toml │ ├── rinja.toml │ └── src │ │ ├── bindings │ │ ├── entrypoint.rs │ │ ├── extensions.rs │ │ ├── gen_cpp │ │ │ ├── config.rs │ │ │ ├── filters.rs │ │ │ ├── mod.rs │ │ │ ├── templates │ │ │ │ ├── BridgingHelper.cpp │ │ │ │ ├── CallbackFunction.cpp │ │ │ │ ├── ForeignFuture.cpp │ │ │ │ ├── Future.cpp │ │ │ │ ├── ObjectHelper.cpp │ │ │ │ ├── RustBufferHelper.cpp │ │ │ │ ├── RustCallStatusHelper.cpp │ │ │ │ ├── StringHelper.cpp │ │ │ │ ├── Struct.cpp │ │ │ │ ├── VTableRegistryHelper.cpp │ │ │ │ ├── entrypoint.cpp │ │ │ │ ├── macros.cpp │ │ │ │ ├── macros.hpp │ │ │ │ ├── wrapper.cpp │ │ │ │ └── wrapper.hpp │ │ │ └── util.rs │ │ ├── gen_rust │ │ │ ├── config.rs │ │ │ ├── extensions.rs │ │ │ ├── mod.rs │ │ │ └── util.rs │ │ ├── gen_typescript │ │ │ ├── callback_interface.rs │ │ │ ├── compounds.rs │ │ │ ├── config.rs │ │ │ ├── custom.rs │ │ │ ├── enum_.rs │ │ │ ├── filters.rs │ │ │ ├── miscellany.rs │ │ │ ├── mod.rs │ │ │ ├── object.rs │ │ │ ├── oracle.rs │ │ │ ├── primitives.rs │ │ │ ├── record.rs │ │ │ ├── templates │ │ │ │ ├── CallbackInterfaceImpl.ts │ │ │ │ ├── CallbackInterfaceTemplate.ts │ │ │ │ ├── CallbackRuntime.ts │ │ │ │ ├── CustomTypeTemplate.ts │ │ │ │ ├── EnumTemplate.ts │ │ │ │ ├── ErrorTemplate.ts │ │ │ │ ├── ExternalTemplate.ts │ │ │ │ ├── InitializationTemplate.ts │ │ │ │ ├── MapTemplate.ts │ │ │ │ ├── ObjectInterfaceTemplate.ts │ │ │ │ ├── ObjectTemplate.ts │ │ │ │ ├── OptionalTemplate.ts │ │ │ │ ├── RecordTemplate.ts │ │ │ │ ├── SequenceTemplate.ts │ │ │ │ ├── StringHelper.ts │ │ │ │ ├── TaggedEnumTemplate.ts │ │ │ │ ├── TopLevelFunctionTemplate.ts │ │ │ │ ├── Types.ts │ │ │ │ ├── macros.ts │ │ │ │ ├── wrapper-ffi.ts │ │ │ │ └── wrapper.ts │ │ │ ├── util.rs │ │ │ └── variant.rs │ │ ├── metadata.rs │ │ ├── mod.rs │ │ └── type_map.rs │ │ ├── cli.rs │ │ ├── lib.rs │ │ ├── react_native.rs │ │ ├── switches.rs │ │ └── wasm.rs ├── ubrn_cli │ ├── Cargo.toml │ ├── fixtures │ │ ├── defaults │ │ │ ├── package.json │ │ │ ├── ubrn-wasm.monorepo.config.yaml │ │ │ ├── ubrn-wasm.multifeature.config.yaml │ │ │ └── ubrn.config.yaml │ │ ├── distinct-bindings │ │ │ ├── package.json │ │ │ ├── ubrn-with-entrypoint.config.yaml │ │ │ └── ubrn.config.yaml │ │ └── merging-toml │ │ │ ├── Cargo.patch.toml │ │ │ ├── package.json │ │ │ └── ubrn.config.yaml │ ├── rinja.toml │ ├── src │ │ ├── cli.rs │ │ ├── codegen │ │ │ └── mod.rs │ │ ├── commands │ │ │ ├── args.rs │ │ │ ├── building.rs │ │ │ ├── checkout.rs │ │ │ ├── generate.rs │ │ │ └── mod.rs │ │ ├── config │ │ │ ├── mod.rs │ │ │ ├── npm.rs │ │ │ └── rust_crate.rs │ │ ├── jsi │ │ │ ├── android │ │ │ │ ├── codegen.rs │ │ │ │ ├── commands.rs │ │ │ │ ├── config.rs │ │ │ │ └── mod.rs │ │ │ ├── bindings.rs │ │ │ ├── codegen.rs │ │ │ ├── crossplatform │ │ │ │ ├── codegen.rs │ │ │ │ ├── config.rs │ │ │ │ └── mod.rs │ │ │ ├── generate.rs │ │ │ ├── ios │ │ │ │ ├── codegen.rs │ │ │ │ ├── commands.rs │ │ │ │ ├── config.rs │ │ │ │ └── mod.rs │ │ │ └── mod.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── test_utils.rs │ │ ├── wasm │ │ │ ├── bindings.rs │ │ │ ├── codegen.rs │ │ │ ├── commands.rs │ │ │ ├── config.rs │ │ │ ├── generate.rs │ │ │ └── mod.rs │ │ └── workspace.rs │ ├── templates │ │ ├── fixtures │ │ │ └── TemplateTester.txt │ │ ├── jsi │ │ │ ├── android │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── ModuleTemplate.java │ │ │ │ ├── ModuleTemplate.kt │ │ │ │ ├── PackageTemplate.java │ │ │ │ ├── PackageTemplate.kt │ │ │ │ ├── build.gradle │ │ │ │ ├── build.kt.gradle │ │ │ │ └── cpp-adapter.cpp │ │ │ ├── crossplatform │ │ │ │ ├── NativeCodegenTemplate.ts │ │ │ │ ├── TurboModuleTemplate.cpp │ │ │ │ ├── TurboModuleTemplate.h │ │ │ │ └── index.tsx │ │ │ └── ios │ │ │ │ ├── ModuleTemplate.h │ │ │ │ ├── ModuleTemplate.mm │ │ │ │ └── module-template.podspec │ │ └── wasm │ │ │ ├── Cargo.toml │ │ │ ├── index.web.ts │ │ │ └── lib.rs │ └── tests │ │ ├── happy_path.rs │ │ ├── mod.rs │ │ └── web_variants.rs ├── ubrn_cli_testing │ ├── Cargo.toml │ └── src │ │ ├── cli.rs │ │ ├── command.rs │ │ ├── command_assertions.rs │ │ ├── file.rs │ │ ├── file_assertions.rs │ │ ├── fixture.rs │ │ ├── lib.rs │ │ ├── recording.rs │ │ └── tests │ │ ├── command_tests.rs │ │ ├── file_shim_str_tests.rs │ │ ├── file_shim_tests.rs │ │ ├── file_tests.rs │ │ ├── fixture_tests.rs │ │ └── mod.rs ├── ubrn_common │ ├── Cargo.toml │ └── src │ │ ├── commands.rs │ │ ├── files.rs │ │ ├── fmt.rs │ │ ├── lib.rs │ │ ├── rust_crate.rs │ │ ├── serde.rs │ │ └── testing.rs ├── ubrn_testing │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── thread.rs │ │ └── timer │ │ ├── mod.rs │ │ ├── native.rs │ │ └── wasm.rs └── uniffi-runtime-javascript │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── lib.rs │ └── wasm32.rs ├── docs ├── assets │ └── css │ │ └── mdbook-admonish.css ├── book.toml └── src │ ├── README.md │ ├── SUMMARY.md │ ├── contributing │ ├── changing-bindings-templates.md │ ├── changing-turbo-module-templates.md │ ├── cutting-a-release.md │ ├── documentation.md │ ├── local-development.md │ └── testing_the_command_line.md │ ├── guides │ ├── getting-started.md │ ├── megazords.md │ ├── pre-installation.md │ ├── publishing.md │ └── troubleshooting.md │ ├── idioms │ ├── async-callbacks.md │ ├── callback-interfaces.md │ ├── common-types.md │ ├── enums.md │ ├── errors.md │ ├── gc.md │ ├── objects.md │ ├── option-result.md │ ├── promises.md │ ├── records.md │ └── types.md │ ├── images │ ├── react-native-logo.svg │ └── rust-logo.svg │ ├── internals │ ├── lifting-and-lowering.md │ └── rn-codegen.md │ └── reference │ ├── commandline.md │ ├── config-yaml.md │ ├── potential-collisions.md │ ├── reserved-words.md │ ├── turbo-module-files.md │ └── uniffi-toml.md ├── fixtures ├── arithmetic-procmacro │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ └── bindings │ │ ├── .supported-flavors.txt │ │ └── test_arithmetic_procmacro.ts ├── arithmetic │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ ├── arithmetic.udl │ │ └── lib.rs │ └── tests │ │ └── bindings │ │ ├── .supported-flavors.txt │ │ └── test_arithmetic.ts ├── async-callbacks │ ├── Cargo.toml │ ├── README.md │ ├── src │ │ └── lib.rs │ ├── tests │ │ ├── bindings │ │ │ ├── .supported-flavors.txt │ │ │ └── test_async_callbacks.ts │ │ └── test_generated_bindings.rs │ └── uniffi.toml ├── async-calls │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── src │ │ ├── async-calls.udl │ │ ├── bin.rs │ │ ├── lib.rs │ │ └── platform_specific │ │ │ ├── mod.rs │ │ │ ├── native.rs │ │ │ └── wasm.rs │ ├── tests │ │ ├── bindings │ │ │ ├── .supported-flavors.txt │ │ │ └── test_async_calls.ts │ │ └── test_generated_bindings.rs │ └── uniffi.toml ├── async-traits │ ├── Cargo.toml │ ├── README.md │ ├── src │ │ └── lib.rs │ ├── tests │ │ ├── bindings │ │ │ ├── .supported-flavors.txt │ │ │ └── test_async_traits.ts │ │ └── test_generated_bindings.rs │ └── uniffi.toml ├── callbacks-example │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── src │ │ ├── callbacks.udl │ │ └── lib.rs │ ├── tests │ │ ├── bindings │ │ │ ├── .supported-flavors.txt │ │ │ └── test_callbacks_example.ts │ │ └── test_generated_bindings.rs │ └── uniffi.toml ├── callbacks-regression │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ ├── tests │ │ ├── bindings │ │ │ ├── .supported-flavors.txt │ │ │ └── test_callbacks_regression.ts │ │ └── test_generated_bindings.rs │ └── uniffi.toml ├── callbacks │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── src │ │ ├── callbacks.udl │ │ └── lib.rs │ └── tests │ │ └── bindings │ │ ├── .supported-flavors.txt │ │ └── test_callbacks.ts ├── chronological │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── src │ │ ├── chronological.udl │ │ └── lib.rs │ └── tests │ │ ├── bindings │ │ ├── .supported-flavors.txt │ │ └── test_chronological.ts │ │ └── test_generated_bindings.rs ├── coverall │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── src │ │ ├── coverall.udl │ │ ├── lib.rs │ │ └── traits.rs │ └── tests │ │ └── bindings │ │ ├── .supported-flavors.txt │ │ └── test_coverall.ts ├── coverall2 │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ ├── bindings │ │ ├── .supported-flavors.txt │ │ └── test_coverall2.ts │ │ └── test_generated_bindings.rs ├── custom-types-example │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ ├── custom-types.udl │ │ └── lib.rs │ ├── tests │ │ ├── bindings │ │ │ ├── .supported-flavors.txt │ │ │ └── test_custom_types_example.ts │ │ └── test_generated_bindings.rs │ └── uniffi.toml ├── enum-types │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ ├── enum_types.udl │ │ └── lib.rs │ └── tests │ │ ├── bindings │ │ ├── .supported-flavors.txt │ │ └── test_enum_types.ts │ │ └── test_generated_bindings.rs ├── error-types │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── src │ │ ├── error_types.udl │ │ └── lib.rs │ └── tests │ │ ├── bindings │ │ ├── .supported-flavors.txt │ │ └── test_error_types.ts │ │ └── test_generated_bindings.rs ├── ext-types │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ ├── ext-types-lib.udl │ │ └── lib.rs │ ├── subcrates │ │ ├── custom-types │ │ │ ├── Cargo.toml │ │ │ ├── build.rs │ │ │ ├── src │ │ │ │ ├── custom_types.udl │ │ │ │ └── lib.rs │ │ │ └── tests │ │ │ │ ├── bindings │ │ │ │ └── test_guid.py │ │ │ │ └── test_generated_bindings.rs │ │ ├── external-crate │ │ │ ├── Cargo.toml │ │ │ ├── README.md │ │ │ └── src │ │ │ │ └── lib.rs │ │ ├── sub-lib │ │ │ ├── Cargo.toml │ │ │ ├── README.md │ │ │ ├── src │ │ │ │ └── lib.rs │ │ │ └── uniffi.toml │ │ └── uniffi-one │ │ │ ├── Cargo.toml │ │ │ ├── build.rs │ │ │ └── src │ │ │ ├── lib.rs │ │ │ └── uniffi-one.udl │ ├── tests │ │ ├── bindings │ │ │ ├── .supported-flavors.txt │ │ │ └── test_ext_types.ts │ │ └── test_generated_bindings.rs │ └── uniffi.toml ├── futures-example │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ ├── bindings │ │ ├── .supported-flavors.txt │ │ └── test_futures_example.ts │ │ └── test_generated_bindings.rs ├── futures │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── src │ │ ├── bin.rs │ │ ├── futures.udl │ │ └── lib.rs │ ├── tests │ │ ├── bindings │ │ │ ├── .supported-flavors.txt │ │ │ └── test_futures.ts │ │ └── test_generated_bindings.rs │ └── uniffi.toml ├── gc-callbacks-crasher │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ └── bindings │ │ ├── .supported-flavors.txt │ │ └── test_gc_callbacks_crasher.ts ├── rondpoint-procmacro │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ ├── bindings │ │ ├── .supported-flavors.txt │ │ └── test_rondpoint_procmacro.ts │ │ └── test_generated_bindings.rs ├── rondpoint │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ ├── lib.rs │ │ └── rondpoint.udl │ └── tests │ │ ├── bindings │ │ ├── .supported-flavors.txt │ │ └── test_rondpoint.ts │ │ └── test_generated_bindings.rs └── trait-methods │ ├── Cargo.toml │ ├── build.rs │ ├── src │ ├── lib.rs │ └── trait_methods.udl │ └── tests │ ├── bindings │ ├── .supported-flavors.txt │ └── test_trait_methods.ts │ └── test_generated_bindings.rs ├── integration └── fixtures │ ├── compat │ ├── package.json │ ├── react-native.config.js │ └── ubrn.config.yaml │ └── turbo-module-testing │ ├── App.tsx │ └── ubrn.config.yaml ├── package.json ├── scripts ├── run-bootstrap-docs.sh ├── run-checkout-tests.sh ├── run-tests.sh └── test-turbo-modules.sh ├── tsconfig.json ├── typescript ├── src │ ├── async-callbacks.ts │ ├── async-rust-call.ts │ ├── callbacks.ts │ ├── enums.ts │ ├── errors.ts │ ├── ffi-converters.ts │ ├── ffi-types.ts │ ├── handle-map.ts │ ├── index.ts │ ├── objects.ts │ ├── records.ts │ ├── result.ts │ ├── rust-call.ts │ ├── symbols.ts │ └── type-utils.ts ├── testing │ ├── asserts.ts │ ├── converters.ts │ ├── hermes.ts │ ├── polyfills.ts │ └── simulated.ts ├── tests │ ├── abort-controller.test.ts │ ├── enums.test.ts │ ├── errors.test.ts │ ├── event-loop.test.ts │ ├── ffi-converters.test.ts │ ├── importing-qualified.test.ts │ ├── importing.test.ts │ ├── playground │ │ ├── enums.ts │ │ ├── exported.ts │ │ └── records.ts │ └── records.test.ts └── tsconfig.template.json ├── uniffi-bindgen-react-native.podspec ├── xtask ├── Cargo.toml └── src │ ├── bootstrap │ ├── hermes.rs │ ├── mod.rs │ ├── test_runner.rs │ └── yarn.rs │ ├── clean.rs │ ├── fmt │ └── mod.rs │ ├── main.rs │ ├── run │ ├── Cargo.template.toml │ ├── cpp_bindings.rs │ ├── generate_bindings.rs │ ├── jsi.rs │ ├── mod.rs │ ├── nodejs.rs │ ├── rust_crate.rs │ ├── typescript.rs │ └── wasm.rs │ └── util │ └── mod.rs └── yarn.lock /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "--quiet run --package xtask --" 3 | -------------------------------------------------------------------------------- /.clang-format-ignore: -------------------------------------------------------------------------------- 1 | cpp_modules/* 2 | */templates/* 3 | -------------------------------------------------------------------------------- /.github/workflows/cocoapods.yml: -------------------------------------------------------------------------------- 1 | name: Publish to CocoaPods 2 | 3 | on: 4 | release: 5 | types: [published] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | publish-cocoapods: 10 | runs-on: macos-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Set up Ruby 17 | uses: ruby/setup-ruby@v1 18 | with: 19 | ruby-version: "3.3" 20 | 21 | - name: Install CocoaPods 22 | run: gem install cocoapods 23 | 24 | - name: Lint Podspec 25 | run: pod spec lint uniffi-bindgen-react-native.podspec 26 | 27 | - name: Publish to CocoaPods 28 | env: 29 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} 30 | run: pod trunk push uniffi-bindgen-react-native.podspec 31 | -------------------------------------------------------------------------------- /.github/workflows/compat-android-latest.yml: -------------------------------------------------------------------------------- 1 | name: Compat / Android (latest) 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" # Once a day at midnight 6 | pull_request: 7 | branches: ["main"] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | compat: 14 | runs-on: ubuntu-latest 15 | name: "bob latest / rn latest / android" 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: ./.github/actions/test-turbo-modules 20 | with: 21 | platform: android 22 | bobVersion: latest 23 | rnVersion: latest 24 | -------------------------------------------------------------------------------- /.github/workflows/compat-android.yml: -------------------------------------------------------------------------------- 1 | name: Compat / Android 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * 1" # Once a week at midnight on Monday 6 | pull_request: 7 | branches: ["main"] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | compat: 14 | strategy: 15 | matrix: 16 | bob-version: 17 | - 0.49.10 18 | rn-version: 19 | - 0.77.2 20 | - 0.78.2 21 | # Uncomment to debug failures 22 | # fail-fast: false 23 | 24 | runs-on: ubuntu-latest 25 | name: "bob ${{ matrix.bob-version }} / rn ${{ matrix.rn-version }} / android" 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: ./.github/actions/test-turbo-modules 30 | with: 31 | platform: android 32 | bobVersion: ${{ matrix.bob-version }} 33 | rnVersion: ${{ matrix.rn-version }} 34 | -------------------------------------------------------------------------------- /.github/workflows/compat-ios-latest.yml: -------------------------------------------------------------------------------- 1 | name: Compat / iOS (latest) 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" # Once a day at midnight 6 | pull_request: 7 | branches: ["main"] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | compat: 14 | runs-on: macos-latest 15 | name: "bob latest / rn latest / ios" 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: ./.github/actions/test-turbo-modules 20 | with: 21 | platform: ios 22 | bobVersion: latest 23 | rnVersion: latest 24 | -------------------------------------------------------------------------------- /.github/workflows/compat-ios.yml: -------------------------------------------------------------------------------- 1 | name: Compat / iOS 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * 1" # Once a week at midnight on Monday 6 | pull_request: 7 | branches: ["main"] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | compat: 14 | strategy: 15 | matrix: 16 | bob-version: 17 | - 0.49.10 18 | rn-version: 19 | - 0.77.2 20 | - 0.78.2 21 | # Uncomment to debug failures 22 | # fail-fast: false 23 | 24 | runs-on: macos-latest 25 | name: "bob ${{ matrix.bob-version }} / rn ${{ matrix.rn-version }} / ios" 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: ./.github/actions/test-turbo-modules 30 | with: 31 | platform: ios 32 | bobVersion: ${{ matrix.bob-version }} 33 | rnVersion: ${{ matrix.rn-version }} 34 | -------------------------------------------------------------------------------- /.github/workflows/crates-io.yaml: -------------------------------------------------------------------------------- 1 | name: Publish to 🦀 crates.io 2 | on: 3 | release: 4 | types: [published] 5 | workflow_dispatch: 6 | inputs: 7 | dry_run: 8 | description: "Perform a dry run (no actual publish)" 9 | type: boolean 10 | default: true 11 | 12 | env: 13 | CARGO_MANIFEST_PATH: crates/uniffi-runtime-javascript/Cargo.toml 14 | 15 | jobs: 16 | publish-crates-io: 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: read 20 | id-token: write 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - name: Verify package 25 | run: cargo package --manifest-path ${{ env.CARGO_MANIFEST_PATH }} --allow-dirty 26 | 27 | - name: Publish (dry run) 28 | if: ${{ github.event.inputs.dry_run == 'true' }} 29 | run: cargo publish --manifest-path ${{ env.CARGO_MANIFEST_PATH }} --dry-run 30 | env: 31 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 32 | 33 | - name: Publish to crates.io 34 | if: ${{ github.event.inputs.dry_run != 'true' }} 35 | run: cargo publish --manifest-path ${{ env.CARGO_MANIFEST_PATH }} 36 | env: 37 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 38 | -------------------------------------------------------------------------------- /.github/workflows/npm.yml: -------------------------------------------------------------------------------- 1 | name: Publish to npmjs 2 | on: 3 | release: 4 | types: [published] 5 | workflow_dispatch: 6 | 7 | jobs: 8 | publish-npmjs: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: read 12 | id-token: write 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: "20.x" 18 | registry-url: "https://registry.npmjs.org" 19 | - run: npm install 20 | - run: npm publish --provenance --access public 21 | env: 22 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Generated by Typescript 7 | dist/ 8 | 9 | # For cpp 10 | cpp_modules/ 11 | build-debug/ 12 | build/ 13 | 14 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 15 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 16 | Cargo.lock 17 | 18 | # These are backup files generated by rustfmt 19 | **/*.rs.bk 20 | 21 | # MSVC Windows builds of rustc generate these, which store debugging information 22 | *.pdb 23 | 24 | # OSX 25 | # 26 | .DS_Store 27 | 28 | # XDE 29 | .expo/ 30 | 31 | # VSCode 32 | .vscode/ 33 | jsconfig.json 34 | 35 | # Xcode 36 | # 37 | build/ 38 | *.pbxuser 39 | !default.pbxuser 40 | *.mode1v3 41 | !default.mode1v3 42 | *.mode2v3 43 | !default.mode2v3 44 | *.perspectivev3 45 | !default.perspectivev3 46 | xcuserdata 47 | *.xccheckout 48 | *.moved-aside 49 | DerivedData 50 | *.hmap 51 | *.ipa 52 | *.xcuserstate 53 | project.xcworkspace 54 | 55 | # Android/IJ 56 | # 57 | .classpath 58 | .cxx 59 | .gradle 60 | .idea 61 | .project 62 | .settings 63 | local.properties 64 | android.iml 65 | 66 | # JS 67 | node_modules/ 68 | *.bundle.js 69 | 70 | # Uniffi 71 | generated/ 72 | 73 | # mdbook artifacts 74 | docs/book 75 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # For cpp 7 | cpp_modules/ 8 | build-debug/ 9 | build/ 10 | 11 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 12 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 13 | Cargo.lock 14 | 15 | # These are backup files generated by rustfmt 16 | **/*.rs.bk 17 | 18 | # MSVC Windows builds of rustc generate these, which store debugging information 19 | *.pdb 20 | 21 | # OSX 22 | # 23 | .DS_Store 24 | 25 | # XDE 26 | .expo/ 27 | 28 | # VSCode 29 | .vscode/ 30 | jsconfig.json 31 | 32 | # Xcode 33 | # 34 | build/ 35 | *.pbxuser 36 | !default.pbxuser 37 | *.mode1v3 38 | !default.mode1v3 39 | *.mode2v3 40 | !default.mode2v3 41 | *.perspectivev3 42 | !default.perspectivev3 43 | xcuserdata 44 | *.xccheckout 45 | *.moved-aside 46 | DerivedData 47 | *.hmap 48 | *.ipa 49 | *.xcuserstate 50 | project.xcworkspace 51 | 52 | # Android/IJ 53 | # 54 | .classpath 55 | .cxx 56 | .gradle 57 | .idea 58 | .project 59 | .settings 60 | local.properties 61 | android.iml 62 | 63 | # JS 64 | node_modules/ 65 | *.bundle.js 66 | 67 | # Uniffi 68 | generated/ 69 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | tsconfig.json 2 | templates/ 3 | build/ 4 | *.template.json 5 | *.md 6 | package.json 7 | *.js.inc 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Community Participation Guidelines 2 | 3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines. 4 | For more details, please read the 5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). 6 | 7 | ## How to Report 8 | For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page. 9 | 10 | ## Project Specific Etiquette 11 | 12 | ### Our Responsibilities 13 | 14 | Project maintainers are responsible for clarifying the standards of acceptable 15 | behavior and are expected to take appropriate and fair corrective action in 16 | response to any instances of unacceptable behavior. 17 | 18 | Project maintainers have the right and responsibility to remove, edit, or 19 | reject comments, commits, code, wiki edits, issues, and other contributions 20 | that are not aligned to this Code of Conduct, or to ban temporarily or 21 | permanently any contributor for other behaviors that they deem inappropriate, 22 | threatening, offensive, or harmful. 23 | 24 | Project maintainers who do not follow or enforce Mozilla's Participation Guidelines in good 25 | faith may face temporary or permanent repercussions. 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "crates/*", 4 | "fixtures/*", 5 | "xtask", 6 | ] 7 | 8 | resolver = "2" 9 | 10 | [workspace.dependencies] 11 | anyhow = "1" 12 | camino = "1.1.6" 13 | cargo_metadata = "0.15" 14 | clap = { version = "4.5.4", features = ["derive"] } 15 | extend = "1.2.0" 16 | heck = "0.5.0" 17 | paste = "1.0.14" 18 | pathdiff = { version = "0.2.1", features = ["camino"] } 19 | rinja = "0.3.5" 20 | serde = { version = "1", features = ["derive"] } 21 | toml = "0.8.22" 22 | uniffi = "=0.29.0" 23 | uniffi_bindgen = "=0.29.0" 24 | uniffi_meta = "=0.29.0" 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This Source Code Form is subject to the terms of the Mozilla Public 2 | License, v. 2.0. If a copy of the MPL was not distributed with this 3 | file, You can obtain one at http://mozilla.org/MPL/2.0/ 4 | -------------------------------------------------------------------------------- /cpp/includes/Bridging.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | namespace uniffi_jsi { 9 | 10 | // Declare the Bridging template 11 | template struct Bridging; 12 | 13 | /* ArrayBuffer constructor expects MutableBuffer*/ 14 | class CMutableBuffer : public jsi::MutableBuffer { 15 | public: 16 | CMutableBuffer(uint8_t *data, size_t len) : _data(data), len(len) {} 17 | size_t size() const override { return len; } 18 | uint8_t *data() override { return _data; } 19 | 20 | private: 21 | uint8_t *_data; 22 | size_t len; 23 | }; 24 | 25 | } // namespace uniffi_jsi 26 | -------------------------------------------------------------------------------- /cpp/includes/Float32.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "UniffiCallInvoker.h" 10 | #include 11 | 12 | namespace uniffi_jsi { 13 | using namespace facebook; 14 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 15 | 16 | template <> struct Bridging { 17 | static float fromJs(jsi::Runtime &rt, std::shared_ptr, 18 | const jsi::Value &value) { 19 | try { 20 | auto v = value.getNumber(); 21 | return static_cast(v); 22 | } catch (const std::logic_error &e) { 23 | throw jsi::JSError(rt, e.what()); 24 | } 25 | } 26 | 27 | static jsi::Value toJs(jsi::Runtime &rt, std::shared_ptr, 28 | float value) { 29 | auto v = static_cast(value); 30 | return jsi::Value(rt, v); 31 | } 32 | }; 33 | 34 | } // namespace uniffi_jsi 35 | -------------------------------------------------------------------------------- /cpp/includes/Float64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "UniffiCallInvoker.h" 10 | #include 11 | 12 | namespace uniffi_jsi { 13 | using namespace facebook; 14 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 15 | 16 | template <> struct Bridging { 17 | static double fromJs(jsi::Runtime &rt, std::shared_ptr, 18 | const jsi::Value &value) { 19 | try { 20 | return value.getNumber(); 21 | } catch (const std::logic_error &e) { 22 | throw jsi::JSError(rt, e.what()); 23 | } 24 | } 25 | 26 | static jsi::Value toJs(jsi::Runtime &rt, std::shared_ptr, 27 | double value) { 28 | return jsi::Value(rt, value); 29 | } 30 | }; 31 | 32 | } // namespace uniffi_jsi 33 | -------------------------------------------------------------------------------- /cpp/includes/ForeignBytes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include 10 | 11 | struct ForeignBytes { 12 | int32_t len; 13 | uint8_t *data; 14 | }; 15 | -------------------------------------------------------------------------------- /cpp/includes/Int16.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "UniffiCallInvoker.h" 10 | #include 11 | 12 | namespace uniffi_jsi { 13 | using namespace facebook; 14 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 15 | 16 | template <> struct Bridging { 17 | static int16_t fromJs(jsi::Runtime &rt, std::shared_ptr, 18 | const jsi::Value &value) { 19 | try { 20 | auto v = value.getNumber(); 21 | return static_cast(v); 22 | } catch (const std::logic_error &e) { 23 | throw jsi::JSError(rt, e.what()); 24 | } 25 | } 26 | 27 | static jsi::Value toJs(jsi::Runtime &rt, std::shared_ptr, 28 | int16_t value) { 29 | auto v = static_cast(value); 30 | return jsi::Value(rt, v); 31 | } 32 | }; 33 | 34 | } // namespace uniffi_jsi 35 | -------------------------------------------------------------------------------- /cpp/includes/Int32.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "UniffiCallInvoker.h" 10 | #include 11 | 12 | namespace uniffi_jsi { 13 | using namespace facebook; 14 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 15 | 16 | template <> struct Bridging { 17 | static int32_t fromJs(jsi::Runtime &rt, std::shared_ptr, 18 | const jsi::Value &value) { 19 | try { 20 | auto v = value.getNumber(); 21 | return static_cast(v); 22 | } catch (const std::logic_error &e) { 23 | throw jsi::JSError(rt, e.what()); 24 | } 25 | } 26 | 27 | static jsi::Value toJs(jsi::Runtime &rt, std::shared_ptr, 28 | int32_t value) { 29 | auto v = static_cast(value); 30 | return jsi::Value(rt, v); 31 | } 32 | }; 33 | 34 | } // namespace uniffi_jsi 35 | -------------------------------------------------------------------------------- /cpp/includes/Int64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "UniffiCallInvoker.h" 10 | #include 11 | 12 | namespace uniffi_jsi { 13 | using namespace facebook; 14 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 15 | 16 | template <> struct Bridging { 17 | static int64_t fromJs(jsi::Runtime &rt, std::shared_ptr, 18 | const jsi::Value &value) { 19 | try { 20 | return value.getBigInt(rt).asInt64(rt); 21 | } catch (const std::logic_error &e) { 22 | throw jsi::JSError(rt, e.what()); 23 | } 24 | } 25 | 26 | static jsi::Value toJs(jsi::Runtime &rt, std::shared_ptr, 27 | int64_t value) { 28 | auto v = jsi::BigInt::fromInt64(rt, value); 29 | return jsi::Value(rt, v); 30 | } 31 | }; 32 | 33 | } // namespace uniffi_jsi 34 | -------------------------------------------------------------------------------- /cpp/includes/Int8.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "UniffiCallInvoker.h" 10 | #include 11 | 12 | namespace uniffi_jsi { 13 | using namespace facebook; 14 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 15 | 16 | template <> struct Bridging { 17 | static int8_t fromJs(jsi::Runtime &rt, std::shared_ptr, 18 | const jsi::Value &value) { 19 | try { 20 | auto v = value.getNumber(); 21 | return static_cast(v); 22 | } catch (const std::logic_error &e) { 23 | throw jsi::JSError(rt, e.what()); 24 | } 25 | } 26 | 27 | static jsi::Value toJs(jsi::Runtime &rt, std::shared_ptr, 28 | int8_t value) { 29 | auto v = static_cast(value); 30 | return jsi::Value(rt, v); 31 | } 32 | }; 33 | 34 | } // namespace uniffi_jsi 35 | -------------------------------------------------------------------------------- /cpp/includes/ReferenceHolder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "UniffiCallInvoker.h" 10 | #include 11 | 12 | template struct ReferenceHolder { 13 | T pointee; 14 | }; 15 | 16 | namespace uniffi_jsi { 17 | using namespace facebook; 18 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 19 | 20 | template struct Bridging> { 21 | static jsi::Value jsNew(jsi::Runtime &rt) { 22 | auto holder = jsi::Object(rt); 23 | return holder; 24 | } 25 | static T fromJs(jsi::Runtime &rt, std::shared_ptr callInvoker, 26 | const jsi::Value &value) { 27 | auto obj = value.asObject(rt); 28 | if (obj.hasProperty(rt, "pointee")) { 29 | auto pointee = obj.getProperty(rt, "pointee"); 30 | return uniffi_jsi::Bridging::fromJs(rt, callInvoker, pointee); 31 | } 32 | throw jsi::JSError(rt, 33 | "Expected ReferenceHolder to have a pointee property. " 34 | "This is likely a bug in uniffi-bindgen-react-native"); 35 | } 36 | }; 37 | 38 | } // namespace uniffi_jsi 39 | -------------------------------------------------------------------------------- /cpp/includes/RustBuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "ForeignBytes.h" 10 | #include "UniffiCallInvoker.h" 11 | #include 12 | 13 | struct RustBuffer { 14 | size_t capacity; 15 | size_t len; 16 | uint8_t *data; 17 | }; 18 | -------------------------------------------------------------------------------- /cpp/includes/Uint16.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "UniffiCallInvoker.h" 10 | #include 11 | 12 | namespace uniffi_jsi { 13 | using namespace facebook; 14 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 15 | 16 | template <> struct Bridging { 17 | static uint16_t fromJs(jsi::Runtime &rt, std::shared_ptr, 18 | const jsi::Value &value) { 19 | try { 20 | auto v = value.getNumber(); 21 | return static_cast(v); 22 | } catch (const std::logic_error &e) { 23 | throw jsi::JSError(rt, e.what()); 24 | } 25 | } 26 | 27 | static jsi::Value toJs(jsi::Runtime &rt, std::shared_ptr, 28 | uint16_t value) { 29 | auto v = static_cast(value); 30 | return jsi::Value(rt, v); 31 | } 32 | }; 33 | 34 | } // namespace uniffi_jsi 35 | -------------------------------------------------------------------------------- /cpp/includes/Uint32.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "UniffiCallInvoker.h" 10 | #include 11 | 12 | namespace uniffi_jsi { 13 | using namespace facebook; 14 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 15 | 16 | template <> struct Bridging { 17 | static uint32_t fromJs(jsi::Runtime &rt, std::shared_ptr, 18 | const jsi::Value &value) { 19 | try { 20 | auto v = value.getNumber(); 21 | return static_cast(v); 22 | } catch (const std::logic_error &e) { 23 | throw jsi::JSError(rt, e.what()); 24 | } 25 | } 26 | 27 | static jsi::Value toJs(jsi::Runtime &rt, std::shared_ptr, 28 | uint32_t value) { 29 | auto v = static_cast(value); 30 | return jsi::Value(rt, v); 31 | } 32 | }; 33 | 34 | } // namespace uniffi_jsi 35 | -------------------------------------------------------------------------------- /cpp/includes/Uint64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "UniffiCallInvoker.h" 10 | #include 11 | 12 | namespace uniffi_jsi { 13 | using namespace facebook; 14 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 15 | 16 | template <> struct Bridging { 17 | static uint64_t fromJs(jsi::Runtime &rt, std::shared_ptr, 18 | const jsi::Value &value) { 19 | try { 20 | return value.getBigInt(rt).asUint64(rt); 21 | } catch (const std::logic_error &e) { 22 | throw jsi::JSError(rt, e.what()); 23 | } 24 | } 25 | 26 | static jsi::Value toJs(jsi::Runtime &rt, std::shared_ptr, 27 | uint64_t value) { 28 | auto v = jsi::BigInt::fromUint64(rt, value); 29 | return jsi::Value(rt, v); 30 | } 31 | }; 32 | 33 | } // namespace uniffi_jsi 34 | -------------------------------------------------------------------------------- /cpp/includes/Uint8.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "UniffiCallInvoker.h" 10 | #include 11 | 12 | namespace uniffi_jsi { 13 | using namespace facebook; 14 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 15 | 16 | template <> struct Bridging { 17 | static uint8_t fromJs(jsi::Runtime &rt, std::shared_ptr, 18 | const jsi::Value &value) { 19 | try { 20 | auto v = value.getNumber(); 21 | return static_cast(v); 22 | } catch (const std::logic_error &e) { 23 | throw jsi::JSError(rt, e.what()); 24 | } 25 | } 26 | 27 | static jsi::Value toJs(jsi::Runtime &rt, std::shared_ptr, 28 | uint8_t value) { 29 | auto v = static_cast(value); 30 | return jsi::Value(rt, v); 31 | } 32 | }; 33 | 34 | } // namespace uniffi_jsi 35 | -------------------------------------------------------------------------------- /cpp/includes/UniffiByteArray.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include 10 | 11 | namespace uniffi_jsi { 12 | using namespace facebook; 13 | 14 | template <> struct Bridging { 15 | static jsi::ArrayBuffer value_to_arraybuffer(jsi::Runtime &rt, 16 | const jsi::Value &value) { 17 | try { 18 | return value.asObject(rt) 19 | .getPropertyAsObject(rt, "buffer") 20 | .getArrayBuffer(rt); 21 | } catch (const std::logic_error &e) { 22 | throw jsi::JSError(rt, e.what()); 23 | } 24 | } 25 | 26 | static jsi::Value arraybuffer_to_value(jsi::Runtime &rt, 27 | const jsi::ArrayBuffer &arrayBuffer) { 28 | try { 29 | jsi::Object obj(rt); 30 | obj.setProperty(rt, "buffer", arrayBuffer); 31 | return jsi::Value(rt, obj); 32 | } catch (const std::logic_error &e) { 33 | throw jsi::JSError(rt, e.what()); 34 | } 35 | } 36 | }; 37 | 38 | } // namespace uniffi_jsi 39 | -------------------------------------------------------------------------------- /cpp/includes/UniffiJsiTypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #pragma once 7 | 8 | #include "Bridging.h" 9 | #include "Int16.h" 10 | #include "Int32.h" 11 | #include "Int64.h" 12 | #include "Int8.h" 13 | #include "Uint16.h" 14 | #include "Uint32.h" 15 | #include "Uint64.h" 16 | #include "Uint8.h" 17 | 18 | #include "Float32.h" 19 | #include "Float64.h" 20 | 21 | #include "ForeignBytes.h" 22 | #include "ReferenceHolder.h" 23 | #include "RustBuffer.h" 24 | #include "UniffiByteArray.h" 25 | #include "UniffiCallInvoker.h" 26 | #include "UniffiRustCallStatus.h" 27 | #include "UniffiString.h" 28 | 29 | #include "RustArcPtr.h" 30 | -------------------------------------------------------------------------------- /cpp/includes/UniffiRustCallStatus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | constexpr int8_t UNIFFI_CALL_STATUS_OK = 0; 7 | constexpr int8_t UNIFFI_CALL_STATUS_ERROR = 1; 8 | constexpr int8_t UNIFFI_CALL_STATUS_PANIC = 2; 9 | 10 | struct RustCallStatus { 11 | int8_t code; 12 | RustBuffer error_buf; 13 | }; 14 | -------------------------------------------------------------------------------- /cpp/includes/registerNatives.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | /// Register host functions into the given runtime. 13 | extern "C" void 14 | registerNatives(facebook::jsi::Runtime &rt, 15 | std::shared_ptr callInvoker); 16 | -------------------------------------------------------------------------------- /cpp/test-harness/README.md: -------------------------------------------------------------------------------- 1 | # test-runner 2 | 3 | Hermes and JSI demo of JSI Host Functions loaded dynamically from 4 | shared libraries specified on the command line. 5 | 6 | `test-runner` dynamically loads shared libraries specified on the command line 7 | after the input JS files, and calls them to register any native functions into 8 | the global object. Then it executes the input JS file, which can use the registered 9 | natives. 10 | 11 | It is a lightly edited version taken from the CC0 licenced [`hf-runner`](https://github.com/tmikov/hermes-jsi-demos/tree/master/hf-runner). 12 | 13 | ## Usage 14 | 15 | ``` 16 | test-runner [ ...] 17 | ``` 18 | 19 | ## Shared Library API 20 | 21 | The shared libraries must export a function named `registerNatives` with the following 22 | signature: 23 | 24 | ```c 25 | void registerNatives(facebook::jsi::Runtime &rt); 26 | ``` 27 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ubrn_bindgen" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [features] 8 | default = [] 9 | wasm = ["quote", "prettyplease", "syn", "proc-macro2"] 10 | 11 | [dependencies] 12 | anyhow = { workspace = true } 13 | camino = { workspace = true } 14 | cargo_metadata = { workspace = true } 15 | clap = { workspace = true } 16 | extend = { workspace = true } 17 | heck = { workspace = true } 18 | paste = { workspace = true } 19 | rinja = { workspace = true } 20 | serde = { workspace = true } 21 | textwrap = "0.16.1" 22 | toml = "0.5" 23 | topological-sort = "0.2.2" 24 | ubrn_common = { path = "../ubrn_common" } 25 | uniffi_bindgen = { workspace = true } 26 | uniffi_meta = { workspace = true } 27 | 28 | [dependencies.quote] 29 | version = "1.0" 30 | optional = true 31 | 32 | [dependencies.prettyplease] 33 | version = "0.2" 34 | optional = true 35 | 36 | [dependencies.syn] 37 | version = "2.0" 38 | optional = true 39 | 40 | [dependencies.proc-macro2] 41 | version = "1.0" 42 | optional = true 43 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/rinja.toml: -------------------------------------------------------------------------------- 1 | [general] 2 | dirs = ["src/bindings/gen_typescript/templates", "src/bindings/gen_cpp/templates"] 3 | 4 | [[syntax]] 5 | name = "ts" 6 | 7 | [[syntax]] 8 | name = "hpp" 9 | 10 | [[syntax]] 11 | name = "cpp" 12 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/entrypoint.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use anyhow::Result; 7 | use ubrn_common::CrateMetadata; 8 | 9 | use crate::{AbiFlavor, ModuleMetadata, SwitchArgs}; 10 | 11 | use super::gen_cpp; 12 | #[cfg(feature = "wasm")] 13 | use super::gen_rust; 14 | 15 | pub fn generate_entrypoint( 16 | switches: &SwitchArgs, 17 | crate_: &CrateMetadata, 18 | modules: &Vec, 19 | ) -> Result { 20 | match &switches.flavor { 21 | AbiFlavor::Jsi => gen_cpp::generate_entrypoint(crate_, modules), 22 | #[cfg(feature = "wasm")] 23 | AbiFlavor::Wasm => gen_rust::generate_entrypoint(crate_, modules), 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_cpp/config.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use serde::{Deserialize, Serialize}; 8 | 9 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 10 | #[serde(rename_all = "camelCase")] 11 | pub(crate) struct CppConfig {} 12 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_cpp/templates/BridgingHelper.cpp: -------------------------------------------------------------------------------- 1 | 2 | namespace {{ ci.cpp_namespace() }} { 3 | template struct Bridging; 4 | 5 | using namespace facebook; 6 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 7 | 8 | template struct Bridging> { 9 | static jsi::Value jsNew(jsi::Runtime &rt) { 10 | auto holder = jsi::Object(rt); 11 | return holder; 12 | } 13 | static T fromJs(jsi::Runtime &rt, std::shared_ptr callInvoker, 14 | const jsi::Value &value) { 15 | auto obj = value.asObject(rt); 16 | if (obj.hasProperty(rt, "pointee")) { 17 | auto pointee = obj.getProperty(rt, "pointee"); 18 | return {{ ci.cpp_namespace() }}::Bridging::fromJs(rt, callInvoker, pointee); 19 | } 20 | throw jsi::JSError( 21 | rt, 22 | "Expected ReferenceHolder to have a pointee property. This is likely a bug in uniffi-bindgen-react-native" 23 | ); 24 | } 25 | }; 26 | } // namespace {{ ci.cpp_namespace() }} 27 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_cpp/templates/ForeignFuture.cpp: -------------------------------------------------------------------------------- 1 | {%- let cb_name = callback.name()|ffi_callback_name %} 2 | namespace {{ ci.cpp_namespace() }} { 3 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 4 | 5 | template <> struct Bridging<{{ cb_name }}> { 6 | static jsi::Value toJs(jsi::Runtime &rt, std::shared_ptr callInvoker, {{ cb_name}} rsCallback) { 7 | {%- let cb_id = callback.name()|fmt("--{}") %} 8 | return jsi::Function::createFromHostFunction( 9 | rt, 10 | jsi::PropNameID::forAscii(rt, "{{ cb_id }}"), 11 | {{ callback.arguments().len() }}, 12 | [rsCallback, callInvoker]( 13 | jsi::Runtime &rt, 14 | const jsi::Value &thisValue, 15 | const jsi::Value *arguments, 16 | size_t count) -> jsi::Value 17 | { 18 | return intoRust(rt, callInvoker, thisValue, arguments, count, rsCallback); 19 | } 20 | ); 21 | } 22 | 23 | static jsi::Value intoRust( 24 | jsi::Runtime &rt, 25 | std::shared_ptr callInvoker, 26 | const jsi::Value &thisValue, 27 | const jsi::Value *args, 28 | size_t count, 29 | {{ cb_name}} func) { 30 | // Convert the arguments into the Rust, with Bridging::fromJs, 31 | // then call the rs_callback with those arguments. 32 | {%- call cpp::cpp_fn_rust_caller_body_with_func_name(callback, "func") %} 33 | } 34 | }; 35 | } // namespace {{ ci.cpp_namespace() }} 36 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_cpp/templates/Future.cpp: -------------------------------------------------------------------------------- 1 | {%- let ns = ci.cpp_namespace() %} 2 | {%- let cb_type = FfiType::Callback("RustFutureContinuationCallback".to_string()) %} 3 | namespace {{ ns }} { 4 | using namespace facebook; 5 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 6 | 7 | template <> struct Bridging { 8 | static UniffiRustFutureContinuationCallback fromJs( 9 | jsi::Runtime &rt, 10 | std::shared_ptr callInvoker, 11 | const jsi::Value &value 12 | ) { 13 | try { 14 | return {{ cb_type.borrow()|cpp_namespace(ci) }}::makeCallbackFunction( 15 | rt, 16 | callInvoker, 17 | value 18 | ); 19 | } catch (const std::logic_error &e) { 20 | throw jsi::JSError(rt, e.what()); 21 | } 22 | } 23 | }; 24 | 25 | } // namespace {{ ns }} 26 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_cpp/templates/ObjectHelper.cpp: -------------------------------------------------------------------------------- 1 | {%- import "macros.cpp" as cpp %} 2 | 3 | {%- for obj in ci.object_definitions() %} 4 | {%- let bless = obj.ffi_function_bless_pointer() %} 5 | {%- let free = obj.ffi_object_free() %} 6 | {%- let uint64 = FfiType::UInt64 %} 7 | {%- call cpp::cpp_fn_from_js_decl(bless) %} { 8 | auto pointer = {{ uint64|bridging_class(ci) }}::fromJs(rt, callInvoker, args[0]); 9 | auto static destructor = [](uint64_t p) { 10 | auto pointer = reinterpret_cast(static_cast(p)); 11 | RustCallStatus status = {0}; 12 | {{ free.name() }}(pointer, &status); 13 | }; 14 | auto ptrObj = std::make_shared<{{ ci.cpp_namespace_includes() }}::DestructibleObject>(pointer, destructor); 15 | auto obj = jsi::Object::createFromHostObject(rt, ptrObj); 16 | return jsi::Value(rt, obj); 17 | } 18 | {%- endfor %} 19 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_cpp/templates/StringHelper.cpp: -------------------------------------------------------------------------------- 1 | {%- import "macros.cpp" as cpp %} 2 | 3 | // Utility functions for serialization/deserialization of strings. 4 | {% let func = ci.ffi_function_string_to_bytelength() %} 5 | {%- call cpp::cpp_fn_from_js_decl(func) %} { 6 | return {{ ci.cpp_namespace_includes() }}::Bridging::string_to_bytelength(rt, args[0]); 7 | } 8 | 9 | {% let func = ci.ffi_function_string_to_arraybuffer() %} 10 | {%- call cpp::cpp_fn_from_js_decl(func) %} { 11 | return {{ ci.cpp_namespace_includes() }}::Bridging::string_to_arraybuffer(rt, args[0]); 12 | } 13 | 14 | {% let func = ci.ffi_function_arraybuffer_to_string() %} 15 | {%- call cpp::cpp_fn_from_js_decl(func) %} { 16 | return {{ ci.cpp_namespace_includes() }}::Bridging::arraybuffer_to_string(rt, args[0]); 17 | } 18 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_cpp/templates/Struct.cpp: -------------------------------------------------------------------------------- 1 | {%- let struct_name = ffi_struct.name()|ffi_struct_name %} 2 | namespace {{ ci.cpp_namespace() }} { 3 | using namespace facebook; 4 | using CallInvoker = uniffi_runtime::UniffiCallInvoker; 5 | 6 | template <> struct Bridging<{{ struct_name }}> { 7 | static {{ struct_name }} fromJs(jsi::Runtime &rt, 8 | std::shared_ptr callInvoker, 9 | const jsi::Value &jsValue 10 | ) { 11 | // Check if the input is an object 12 | if (!jsValue.isObject()) { 13 | throw jsi::JSError(rt, "Expected an object for {{ struct_name }}"); 14 | } 15 | 16 | // Get the object from the jsi::Value 17 | auto jsObject = jsValue.getObject(rt); 18 | 19 | // Create the vtable struct 20 | {{ struct_name }} rsObject; 21 | 22 | // Create the vtable from the js callbacks. 23 | {%- for field in ffi_struct.fields() %} 24 | {%- let rs_field_name = field.name() %} 25 | {%- let ts_field_name = field.name()|var_name %} 26 | {%- if field.type_().is_callable() %} 27 | rsObject.{{ rs_field_name }} = {# space #} 28 | {%- call cpp::callback_fn_namespace(ffi_struct, field) -%} 29 | ::makeCallbackFunction( 30 | rt, callInvoker, jsObject.getProperty(rt, "{{ ts_field_name }}") 31 | ); 32 | {%- else %} 33 | rsObject.{{ rs_field_name }} = {# space -#} 34 | {{ field.type_().borrow()|bridging_class(ci) }}::fromJs( 35 | rt, callInvoker, 36 | jsObject.getProperty(rt, "{{ ts_field_name }}") 37 | ); 38 | {%- endif %} 39 | {%- endfor %} 40 | 41 | return rsObject; 42 | } 43 | }; 44 | 45 | } // namespace {{ ci.cpp_namespace() }} 46 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_cpp/templates/entrypoint.cpp: -------------------------------------------------------------------------------- 1 | // This file was autogenerated by some hot garbage in the `uniffi-bindgen-react-native` crate. 2 | // Trust me, you don't want to mess with it! 3 | 4 | // This template is only used when running `cargo xtask run`, i.e. when running `./scripts/run-tests.sh`. 5 | // It is an implementation of the `registerNatives` function which is used by the test-runner to dynamically 6 | // load the native modules without re-compiling the test-runner. 7 | // 8 | // Files generated with this template appear in the fixtures/{fixture}/generated/cpp directory, always called 9 | // `Entrypoint.cpp`, with a captilized `E` to ensure it does not collide with namespaces called "entrypoint". 10 | 11 | #include "registerNatives.h" 12 | 13 | {%- for m in modules %} 14 | #include "{{ m.hpp_filename() }}" 15 | {%- endfor %} 16 | 17 | extern "C" void registerNatives(jsi::Runtime &rt, std::shared_ptr callInvoker) { 18 | {%- for m in modules %} 19 | {{ m.cpp_module() }}::registerModule(rt, callInvoker); 20 | {%- endfor %} 21 | } 22 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_cpp/templates/macros.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhugman/uniffi-bindgen-react-native/2de076e95efafcfa26350eb51ace6cc22f4aff84/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/macros.hpp -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_cpp/util.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use anyhow::Result; 7 | use camino::Utf8Path; 8 | use ubrn_common::{fmt, run_cmd_quietly}; 9 | 10 | pub(crate) fn format_directory(out_dir: &Utf8Path) -> Result<()> { 11 | if let Some(mut clang_format) = fmt::clang_format(out_dir, false)? { 12 | run_cmd_quietly(&mut clang_format)? 13 | } else { 14 | eprintln!("Skipping formatting C++. Is clang-format installed?"); 15 | } 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_rust/config.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use serde::{Deserialize, Serialize}; 8 | 9 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 10 | #[serde(rename_all = "camelCase")] 11 | pub(crate) struct RustConfig {} 12 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_rust/util.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use heck::{ToLowerCamelCase, ToSnakeCase}; 7 | use proc_macro2::Span; 8 | use syn::Ident; 9 | 10 | pub(super) fn if_or_default(flag: bool, then: T) -> T 11 | where 12 | T: Default, 13 | { 14 | if flag { 15 | then 16 | } else { 17 | Default::default() 18 | } 19 | } 20 | 21 | pub(super) fn if_then_map(flag: bool, then: F) -> T 22 | where 23 | F: FnOnce() -> T, 24 | T: Default, 25 | { 26 | if flag { 27 | then() 28 | } else { 29 | Default::default() 30 | } 31 | } 32 | 33 | pub(super) fn map_or_default(value: Option, map: F) -> U 34 | where 35 | F: FnOnce(T) -> U, 36 | U: Default, 37 | { 38 | value.map_or_else(|| Default::default(), map) 39 | } 40 | 41 | pub(super) fn ident(id: &str) -> Ident { 42 | Ident::new(id, Span::call_site()) 43 | } 44 | 45 | pub(super) fn snake_case_ident(s: &str) -> Ident { 46 | ident(&s.to_snake_case()) 47 | } 48 | 49 | pub(super) fn camel_case_ident(field_name: &str) -> Ident { 50 | ident(&field_name.to_lower_camel_case()) 51 | } 52 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/callback_interface.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use uniffi_bindgen::ComponentInterface; 7 | 8 | use super::oracle::{CodeOracle, CodeType}; 9 | 10 | #[derive(Debug)] 11 | pub struct CallbackInterfaceCodeType { 12 | id: String, 13 | } 14 | 15 | impl CallbackInterfaceCodeType { 16 | pub fn new(id: String) -> Self { 17 | Self { id } 18 | } 19 | } 20 | 21 | impl CodeType for CallbackInterfaceCodeType { 22 | fn decl_type_label(&self, ci: &ComponentInterface) -> String { 23 | format!("{}Impl", CodeOracle.class_name(ci, &self.id)) 24 | } 25 | 26 | fn type_label(&self, ci: &ComponentInterface) -> String { 27 | CodeOracle.class_name(ci, &self.id) 28 | } 29 | 30 | fn canonical_name(&self) -> String { 31 | format!("Type{}", self.id) 32 | } 33 | 34 | fn initialization_fn(&self) -> Option { 35 | Some(format!("uniffiCallbackInterface{}.register", self.id)) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/custom.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use super::oracle::{CodeOracle, CodeType}; 7 | use uniffi_bindgen::ComponentInterface; 8 | 9 | #[derive(Debug)] 10 | pub struct CustomCodeType { 11 | name: String, 12 | } 13 | 14 | impl CustomCodeType { 15 | pub fn new(name: String) -> Self { 16 | CustomCodeType { name } 17 | } 18 | } 19 | 20 | impl CodeType for CustomCodeType { 21 | fn type_label(&self, ci: &ComponentInterface) -> String { 22 | CodeOracle.class_name(ci, &self.name) 23 | } 24 | 25 | fn canonical_name(&self) -> String { 26 | format!("Type{}", self.name) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/miscellany.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use super::oracle::CodeType; 7 | use paste::paste; 8 | use uniffi_bindgen::ComponentInterface; 9 | 10 | macro_rules! impl_code_type_for_miscellany { 11 | ($T:ty, $class_name:literal, $canonical_name:literal) => { 12 | paste! { 13 | #[derive(Debug)] 14 | pub struct $T; 15 | 16 | impl CodeType for $T { 17 | fn type_label(&self, _ci: &ComponentInterface) -> String { 18 | $class_name.into() 19 | } 20 | 21 | fn canonical_name(&self) -> String { 22 | $canonical_name.into() 23 | } 24 | } 25 | } 26 | }; 27 | } 28 | 29 | impl_code_type_for_miscellany!(TimestampCodeType, "UniffiTimestamp", "Timestamp"); 30 | 31 | impl_code_type_for_miscellany!(DurationCodeType, "UniffiDuration", "Duration"); 32 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/record.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use super::oracle::{CodeOracle, CodeType}; 8 | use uniffi_bindgen::ComponentInterface; 9 | 10 | #[derive(Debug)] 11 | pub struct RecordCodeType { 12 | id: String, 13 | } 14 | 15 | impl RecordCodeType { 16 | pub fn new(id: String) -> Self { 17 | Self { id } 18 | } 19 | } 20 | 21 | impl CodeType for RecordCodeType { 22 | fn type_label(&self, ci: &ComponentInterface) -> String { 23 | CodeOracle.class_name(ci, &self.id) 24 | } 25 | 26 | fn canonical_name(&self) -> String { 27 | format!("Type{}", self.id) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/templates/CallbackInterfaceTemplate.ts: -------------------------------------------------------------------------------- 1 | {%- let cbi = ci.get_callback_interface_definition(name).expect("Callback Interface definition not found in this ci") %} 2 | {%- let methods = cbi.methods() %} 3 | {%- let protocol_name = type_name.clone() %} 4 | {%- let protocol_docstring = cbi.docstring() %} 5 | {%- let vtable = cbi.vtable() %} 6 | {{- self.import_infra("FfiConverterCallback", "callbacks") }} 7 | {#- obj is used to generate an interface with ObjectInterfaceTemplate.ts #} 8 | {%- let obj = cbi %} 9 | {% include "ObjectInterfaceTemplate.ts" %} 10 | {% include "CallbackInterfaceImpl.ts" %} 11 | 12 | // FfiConverter protocol for callback interfaces 13 | const {{ ffi_converter_name }} = new FfiConverterCallback<{{ type_name }}>(); 14 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/templates/CallbackRuntime.ts: -------------------------------------------------------------------------------- 1 | {%- if ci.has_async_callbacks() %} 2 | // These types are part of the async callback machinery. 3 | // They are discarded at compile time so should not affect the bundle size. 4 | {# space #} 5 | {%- for ffi_struct in ci.iter_ffi_structs_for_callbacks() %} 6 | type {{ ffi_struct.name()|ffi_struct_name }} = { 7 | {%- for field in ffi_struct.fields() %} 8 | {{ field.name()|var_name }}: {{ field.type_().borrow()|ffi_type_name }}; 9 | {%- endfor %} 10 | }; 11 | {%- endfor %} 12 | 13 | {%- for callback in ci.iter_ffi_callback_literals() %} 14 | type {{ callback.name()|ffi_callback_name }} = ( 15 | {%- for arg in callback.arguments_no_return() %} 16 | {{- arg.name()|var_name }}: {{ arg.type_().borrow()|ffi_type_name }}{% if !loop.last %}, {% endif %} 17 | {%- endfor %}) => void; 18 | {%- endfor %} 19 | {%- endif %} 20 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/templates/ExternalTemplate.ts: -------------------------------------------------------------------------------- 1 | {# Generate an import statement for the given type. #} 2 | {{- self.import_external_type(type_) }} 3 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/templates/InitializationTemplate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This should be called before anything else. 3 | * 4 | * It is likely that this is being done for you by the library's `index.ts`. 5 | * 6 | * It checks versions of uniffi between when the Rust scaffolding was generated 7 | * and when the bindings were generated. 8 | * 9 | * It also initializes the machinery to enable Rust to talk back to Javascript. 10 | */ 11 | function uniffiEnsureInitialized() { 12 | {{- self.import_infra("UniffiInternalError", "errors") }} 13 | // Get the bindings contract version from our ComponentInterface 14 | const bindingsContractVersion = {{ ci.uniffi_contract_version() }}; 15 | // Get the scaffolding contract version by calling the into the dylib 16 | const scaffoldingContractVersion = {% call ts::fn_handle(ci.ffi_uniffi_contract_version()) %}(); 17 | if (bindingsContractVersion !== scaffoldingContractVersion) { 18 | throw new UniffiInternalError.ContractVersionMismatch(scaffoldingContractVersion, bindingsContractVersion); 19 | } 20 | 21 | {%- for (name, expected_checksum) in ci.iter_checksums() %} 22 | if ({% call ts::fn_handle_with_name(name) %}() !== {{ expected_checksum }}) { 23 | throw new UniffiInternalError.ApiChecksumMismatch("{{ name }}"); 24 | } 25 | {%- endfor %} 26 | 27 | {% for fn in self.initialization_fns() -%} 28 | {{ fn }}(); 29 | {% endfor -%} 30 | } 31 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/templates/MapTemplate.ts: -------------------------------------------------------------------------------- 1 | {{- self.import_infra("FfiConverterMap", "ffi-converters") }} 2 | {%- let key_ffi_converter = key_type|ffi_converter_name(self) %} 3 | {%- let value_ffi_converter = value_type|ffi_converter_name(self) %} 4 | // FfiConverter for {{ type_name }} 5 | const {{ ffi_converter_name }} = new FfiConverterMap({{ key_ffi_converter }}, {{ value_ffi_converter }}); 6 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/templates/ObjectInterfaceTemplate.ts: -------------------------------------------------------------------------------- 1 | {%- let protocol_docstring = obj.docstring() %} 2 | {%- call ts::docstring_value(protocol_docstring, 0) %} 3 | export interface {{ protocol_name }} { 4 | {% for meth in methods.iter() -%} 5 | {%- call ts::docstring(meth, 4) %} 6 | {{ meth.name()|fn_name }}({% call ts::arg_list_protocol(meth) %}) {% call ts::throws_kw(meth) -%} 7 | : {# space #} 8 | {%- call ts::return_type(meth) %}; 9 | {%- endfor %} 10 | } 11 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/templates/OptionalTemplate.ts: -------------------------------------------------------------------------------- 1 | {{- self.import_infra("FfiConverterOptional", "ffi-converters") }} 2 | {%- let item_ffi_converter = inner_type|ffi_converter_name(self) %} 3 | // FfiConverter for {{ type_name }} 4 | const {{ ffi_converter_name }} = new FfiConverterOptional({{ item_ffi_converter }}); 5 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/templates/SequenceTemplate.ts: -------------------------------------------------------------------------------- 1 | {{- self.import_infra("FfiConverterArray", "ffi-converters") }} 2 | {%- let item_ffi_converter = inner_type|ffi_converter_name(self) %} 3 | // FfiConverter for {{ type_name }} 4 | const {{ ffi_converter_name }} = new FfiConverterArray({{ item_ffi_converter }}); 5 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/templates/StringHelper.ts: -------------------------------------------------------------------------------- 1 | {{- self.import_infra_type("UniffiByteArray", "ffi-types") }} 2 | {{- self.import_infra("uniffiCreateFfiConverterString", "ffi-converters") }} 3 | 4 | {%- if flavor.supports_text_encoder() %} 5 | const stringConverter = (() => { 6 | const encoder = new TextEncoder(); 7 | const decoder = new TextDecoder(); 8 | return { 9 | stringToBytes: (s: string) => encoder.encode(s), 10 | bytesToString: (ab: UniffiByteArray) => decoder.decode(ab), 11 | stringByteLength: (s: string) => encoder.encode(s).byteLength, 12 | }; 13 | })(); 14 | {%- else %} 15 | const stringConverter = { 16 | stringToBytes: (s: string) => 17 | uniffiCaller.rustCall((status) => {% call ts::fn_handle(ci.ffi_function_string_to_arraybuffer()) %}(s, status)), 18 | bytesToString: (ab: UniffiByteArray) => 19 | uniffiCaller.rustCall((status) => {% call ts::fn_handle(ci.ffi_function_arraybuffer_to_string()) %}(ab, status)), 20 | stringByteLength: (s: string) => 21 | uniffiCaller.rustCall((status) => {% call ts::fn_handle(ci.ffi_function_string_to_bytelength()) %}(s, status)), 22 | }; 23 | {%- endif %} 24 | const FfiConverterString = uniffiCreateFfiConverterString(stringConverter); 25 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/templates/TopLevelFunctionTemplate.ts: -------------------------------------------------------------------------------- 1 | {%- call ts::top_func_decl("function", func, 0) %} 2 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/util.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use anyhow::Result; 7 | use camino::Utf8Path; 8 | 9 | use ubrn_common::{fmt, run_cmd_quietly}; 10 | 11 | pub(crate) fn format_directory(out_dir: &Utf8Path) -> Result<()> { 12 | if let Some(mut prettier) = fmt::prettier(out_dir, false)? { 13 | run_cmd_quietly(&mut prettier)? 14 | } else { 15 | eprintln!("No prettier found. Install with `yarn add --dev prettier`"); 16 | } 17 | Ok(()) 18 | } 19 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/gen_typescript/variant.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use super::{AsCodeType, CodeType, CodeOracle}; 7 | use crate::interface::{ComponentInterface, Variant}; 8 | 9 | #[derive(Debug)] 10 | pub(super) struct VariantCodeType { 11 | pub v: Variant, 12 | } 13 | 14 | impl CodeType for VariantCodeType { 15 | fn type_label(&self, ci: &ComponentInterface) -> String { 16 | CodeOracle.class_name(ci, self.v.name()) 17 | } 18 | 19 | fn canonical_name(&self) -> String { 20 | self.v.name().to_string() 21 | } 22 | } 23 | 24 | impl AsCodeType for Variant { 25 | fn as_codetype(&self) -> Box { 26 | Box::new(VariantCodeType { v: self.clone() }) 27 | } 28 | } 29 | 30 | impl AsCodeType for &Variant { 31 | fn as_codetype(&self) -> Box { 32 | Box::new(VariantCodeType { v: (*self).clone() }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | mod entrypoint; 8 | pub(crate) mod extensions; 9 | pub(crate) mod gen_cpp; 10 | #[cfg(feature = "wasm")] 11 | pub(crate) mod gen_rust; 12 | pub(crate) mod gen_typescript; 13 | pub(crate) mod metadata; 14 | pub(crate) mod type_map; 15 | 16 | pub use self::entrypoint::generate_entrypoint; 17 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/bindings/type_map.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use uniffi_bindgen::{interface::Type, Component}; 8 | use uniffi_meta::AsType; 9 | 10 | /// Now vestigial type previously used to handle external types. 11 | /// 12 | /// We keep this here for redirection purposes: all calls through filters 13 | /// goes through the `as_type` method, which makes it a useful place to store state 14 | /// and change types. 15 | #[derive(Default, Debug)] 16 | pub(crate) struct TypeMap {} 17 | 18 | impl TypeMap { 19 | pub(crate) fn as_type(&self, as_type: &impl AsType) -> Type { 20 | as_type.as_type() 21 | } 22 | } 23 | 24 | impl From<&[Component]> for TypeMap { 25 | fn from(_: &[Component]) -> Self { 26 | Self::default() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | mod bindings; 7 | mod cli; 8 | mod react_native; 9 | mod switches; 10 | #[cfg(feature = "wasm")] 11 | mod wasm; 12 | 13 | pub use self::{ 14 | bindings::{generate_entrypoint, metadata::ModuleMetadata}, 15 | cli::{BindingsArgs, OutputArgs, SourceArgs}, 16 | switches::{AbiFlavor, SwitchArgs}, 17 | }; 18 | -------------------------------------------------------------------------------- /crates/ubrn_bindgen/src/switches.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use clap::{Args, ValueEnum}; 8 | 9 | #[derive(Args, Clone, Debug)] 10 | pub struct SwitchArgs { 11 | /// The flavor of bindings to produce. 12 | #[clap(long, default_value = "jsi")] 13 | pub flavor: AbiFlavor, 14 | } 15 | 16 | impl Default for SwitchArgs { 17 | fn default() -> Self { 18 | Self { 19 | flavor: AbiFlavor::Jsi, 20 | } 21 | } 22 | } 23 | 24 | impl SwitchArgs { 25 | pub fn flavor(&self) -> AbiFlavor { 26 | self.flavor.clone() 27 | } 28 | } 29 | 30 | #[derive(Clone, Debug, ValueEnum, PartialEq)] 31 | pub enum AbiFlavor { 32 | Jsi, 33 | #[cfg(feature = "wasm")] 34 | Wasm, 35 | } 36 | 37 | impl AbiFlavor { 38 | pub fn entrypoint(&self) -> &str { 39 | match self { 40 | Self::Jsi => "Entrypoint.cpp", 41 | #[cfg(feature = "wasm")] 42 | Self::Wasm => "src/lib.rs", 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /crates/ubrn_cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-bindgen-react-native" 3 | version = "0.29.0-0" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "ubrn_cli" 8 | path = "src/lib.rs" 9 | 10 | # This explicitly defines the binary 11 | [[bin]] 12 | name = "uniffi-bindgen-react-native" 13 | path = "src/main.rs" 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [features] 17 | default = ["wasm"] 18 | wasm = [] 19 | 20 | [dependencies] 21 | anyhow = { workspace = true } 22 | rinja = { workspace = true } 23 | camino = { workspace = true } 24 | clap = { workspace = true } 25 | extend = { workspace = true } 26 | globset = { version = "0.4.14", features = ["serde1"] } 27 | heck = { workspace = true } 28 | paste = { workspace = true } 29 | pathdiff = { workspace = true } 30 | serde = { workspace = true } 31 | serde-toml-merge = "0.3.9" 32 | textwrap = "0.16.1" 33 | toml = { workspace = true, features = ["display"] } 34 | topological-sort = "0.2.2" 35 | ubrn_bindgen = { path = "../ubrn_bindgen", features = ["wasm"] } 36 | ubrn_common = { path = "../ubrn_common" } 37 | uniffi_bindgen = { workspace = true } 38 | uniffi_meta = { workspace = true } 39 | 40 | [dev-dependencies] 41 | ubrn_cli_testing = { path = "../ubrn_cli_testing" } 42 | -------------------------------------------------------------------------------- /crates/ubrn_cli/fixtures/defaults/ubrn-wasm.monorepo.config.yaml: -------------------------------------------------------------------------------- 1 | rust: 2 | directory: rust/shim 3 | manifestPath: Cargo.toml 4 | web: 5 | manifestPath: rust/crates/wasm-crate/Cargo.toml 6 | features: 7 | - wasm32_only 8 | workspace: true 9 | -------------------------------------------------------------------------------- /crates/ubrn_cli/fixtures/defaults/ubrn-wasm.multifeature.config.yaml: -------------------------------------------------------------------------------- 1 | rust: 2 | directory: rust/shim 3 | manifestPath: Cargo.toml 4 | web: 5 | manifestPath: rust/crates/wasm-crate/Cargo.toml 6 | features: 7 | - wasm32_only 8 | - some_feature 9 | defaultFeatures: false 10 | -------------------------------------------------------------------------------- /crates/ubrn_cli/fixtures/defaults/ubrn.config.yaml: -------------------------------------------------------------------------------- 1 | rust: 2 | directory: rust/shim 3 | manifestPath: Cargo.toml 4 | -------------------------------------------------------------------------------- /crates/ubrn_cli/fixtures/distinct-bindings/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "default-fixture", 3 | "version": "0.1.0", 4 | "description": "An automated test", 5 | "default": "./web-src/index.ts", 6 | "browser": "./web-src/index.ts", 7 | "react-native": "./src/index.tsx", 8 | "scripts": { 9 | "example": "yarn workspace dummy-lib-example", 10 | "test": "jest", 11 | "typecheck": "tsc", 12 | "lint": "eslint \"**/*.{js,ts,tsx}\"", 13 | "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib", 14 | "prepare": "bob build", 15 | "release": "release-it" 16 | }, 17 | "keywords": [ 18 | "react-native", 19 | "ios", 20 | "android" 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/jhugman/dummy-lib.git" 25 | }, 26 | "author": "James (https://nowhere.com/james)", 27 | "license": "MIT", 28 | "packageManager": "yarn@3.6.1", 29 | "prettier": { 30 | "quoteProps": "consistent", 31 | "singleQuote": true, 32 | "tabWidth": 2, 33 | "trailingComma": "es5", 34 | "useTabs": false 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /crates/ubrn_cli/fixtures/distinct-bindings/ubrn-with-entrypoint.config.yaml: -------------------------------------------------------------------------------- 1 | rust: 2 | directory: rust/shim 3 | manifestPath: Cargo.toml 4 | web: 5 | tsBindings: ./bindings/wasm 6 | entrypoint: ./entrypoints/web.ts 7 | -------------------------------------------------------------------------------- /crates/ubrn_cli/fixtures/distinct-bindings/ubrn.config.yaml: -------------------------------------------------------------------------------- 1 | rust: 2 | directory: rust/shim 3 | manifestPath: Cargo.toml 4 | web: 5 | tsBindings: ./web-src/bindings 6 | -------------------------------------------------------------------------------- /crates/ubrn_cli/fixtures/merging-toml/Cargo.patch.toml: -------------------------------------------------------------------------------- 1 | [dependencies] 2 | foo = "PATCHED" 3 | uniffi-example-arithmetic = { no-default-features = true } 4 | wasm-bindgen = "PATCHED" 5 | zzz = "PATCHED" 6 | 7 | 8 | [patch] 9 | patch = { patch = "patch" } 10 | -------------------------------------------------------------------------------- /crates/ubrn_cli/fixtures/merging-toml/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "default-fixture", 3 | "version": "0.1.0", 4 | "description": "An automated test", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/jhugman/dummy-lib.git" 8 | }, 9 | "author": "James (https://nowhere.com/james)" 10 | } 11 | -------------------------------------------------------------------------------- /crates/ubrn_cli/fixtures/merging-toml/ubrn.config.yaml: -------------------------------------------------------------------------------- 1 | rust: 2 | directory: rust/shim 3 | manifestPath: Cargo.toml 4 | web: 5 | manifestPath: rust_modules/wasm/Cargo.toml 6 | manifestPatchFile: config/Cargo.patch.toml 7 | -------------------------------------------------------------------------------- /crates/ubrn_cli/rinja.toml: -------------------------------------------------------------------------------- 1 | [general] 2 | dirs = [ 3 | "templates/fixtures", 4 | "templates/jsi/crossplatform", 5 | "templates/jsi/android", 6 | "templates/jsi/ios", 7 | "templates/wasm", 8 | ] 9 | default_syntax = "none" 10 | 11 | [[syntax]] 12 | name = "none" 13 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/cli.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use anyhow::Result; 7 | use clap::{Parser, Subcommand}; 8 | 9 | use crate::{ 10 | commands::{checkout::GitRepoArgs, BuildArgs, CheckoutArgs, GenerateArgs}, 11 | workspace, AsConfig, 12 | }; 13 | 14 | #[derive(Parser)] 15 | pub struct CliArgs { 16 | #[command(subcommand)] 17 | pub(crate) cmd: CliCmd, 18 | } 19 | 20 | impl CliArgs { 21 | pub fn run(&self) -> Result<()> { 22 | self.cmd.run() 23 | } 24 | } 25 | 26 | #[derive(Debug, Subcommand)] 27 | pub(crate) enum CliCmd { 28 | /// Checkout a given Github repo into `rust_modules` 29 | Checkout(CheckoutArgs), 30 | /// Build (and optionally generate code) for Android or iOS 31 | Build(BuildArgs), 32 | /// Generate bindings or the turbo-module glue code from the Rust. 33 | /// 34 | /// These steps are already performed when building with `--and-generate`. 35 | Generate(GenerateArgs), 36 | } 37 | 38 | impl CliCmd { 39 | pub(crate) fn run(&self) -> Result<()> { 40 | match self { 41 | Self::Checkout(c) => { 42 | AsConfig::::as_config(c)?.checkout(&workspace::project_root()?) 43 | } 44 | Self::Build(b) => b.build(), 45 | Self::Generate(g) => g.run(), 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/commands/args.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use camino::Utf8PathBuf; 7 | use clap::{self, Args}; 8 | 9 | use crate::{workspace, ProjectConfig}; 10 | 11 | #[derive(Args, Clone, Debug)] 12 | pub(crate) struct ConfigArgs { 13 | /// The configuration file for this project 14 | #[clap(long)] 15 | config: Option, 16 | } 17 | 18 | impl ConfigArgs { 19 | pub(crate) fn new(config: Option) -> Self { 20 | Self { config } 21 | } 22 | } 23 | 24 | fn default_config_path() -> Utf8PathBuf { 25 | workspace::ubrn_config_yaml() 26 | .expect("Can't find a ubrn.config.yaml file: specify one with a `--config` argument") 27 | } 28 | 29 | impl Default for ConfigArgs { 30 | fn default() -> Self { 31 | let config = Some(default_config_path()); 32 | Self { config } 33 | } 34 | } 35 | 36 | impl TryFrom for ProjectConfig { 37 | type Error = anyhow::Error; 38 | 39 | fn try_from(value: ConfigArgs) -> Result { 40 | let path = value.config.unwrap_or_else(default_config_path); 41 | Self::try_from(path) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | mod args; 8 | pub(crate) mod building; 9 | pub(crate) mod checkout; 10 | pub(crate) mod generate; 11 | 12 | pub(crate) use args::ConfigArgs; 13 | pub(crate) use building::BuildArgs; 14 | pub(crate) use checkout::CheckoutArgs; 15 | pub(crate) use generate::GenerateArgs; 16 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/jsi/android/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | pub(crate) mod codegen; 7 | pub(crate) mod commands; 8 | pub(crate) mod config; 9 | 10 | pub(crate) use codegen::get_files; 11 | pub(crate) use commands::AndroidBuildArgs; 12 | pub(crate) use config::AndroidConfig; 13 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/jsi/bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use anyhow::Result; 8 | use camino::Utf8PathBuf; 9 | 10 | use ubrn_bindgen::{BindingsArgs, OutputArgs, SourceArgs, SwitchArgs}; 11 | 12 | use crate::config::ProjectConfig; 13 | 14 | pub(crate) fn bindings( 15 | project: &ProjectConfig, 16 | switches: SwitchArgs, 17 | lib_file: &Utf8PathBuf, 18 | ) -> Result { 19 | let root = project.project_root(); 20 | let ts_dir = project.bindings.ts_path(root); 21 | let cpp_dir = project.bindings.cpp_path(root); 22 | 23 | let config = project.bindings.uniffi_toml_path(project.project_root()); 24 | if let Some(ref file) = config { 25 | if !file.exists() { 26 | anyhow::bail!("uniffi.toml file {:?} does not exist. Either delete the uniffiToml property or supply a file", file) 27 | } 28 | } 29 | 30 | Ok(BindingsArgs::new( 31 | switches, 32 | SourceArgs::library(lib_file).with_config(config), 33 | OutputArgs::new(&ts_dir, &cpp_dir, false), 34 | )) 35 | } 36 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/jsi/codegen.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use std::rc::Rc; 7 | 8 | use crate::codegen::{RenderedFile, TemplateConfig}; 9 | use crate::jsi::{android, crossplatform, ios}; 10 | 11 | pub(crate) fn get_files(config: Rc) -> Vec> { 12 | let mut files = Vec::new(); 13 | files.extend(crossplatform::get_files(config.clone())); 14 | files.extend(ios::get_files(config.clone())); 15 | files.extend(android::get_files(config.clone())); 16 | files 17 | } 18 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/jsi/crossplatform/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | pub(crate) mod codegen; 7 | pub(crate) mod config; 8 | 9 | pub(crate) use self::codegen::get_files; 10 | pub(crate) use self::config::TurboModulesConfig; 11 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/jsi/ios/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | pub(crate) mod codegen; 7 | pub(crate) mod commands; 8 | pub(crate) mod config; 9 | 10 | pub(crate) use codegen::get_files; 11 | pub(crate) use commands::IosBuildArgs; 12 | pub(crate) use config::IOsConfig; 13 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/jsi/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | pub(crate) mod android; 7 | mod bindings; 8 | mod codegen; 9 | pub(crate) mod crossplatform; 10 | mod generate; 11 | pub(crate) mod ios; 12 | 13 | pub(crate) use bindings::bindings; 14 | pub(crate) use codegen::get_files; 15 | pub(crate) use generate::{CmdArg, TurboModuleArgs}; 16 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use clap::Parser; 7 | use ubrn_cli::{cli, Result}; 8 | 9 | fn main() -> Result<()> { 10 | let args = cli::CliArgs::parse(); 11 | args.run() 12 | } 13 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/test_utils.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use std::fmt::Display; 8 | 9 | use anyhow::Result; 10 | use camino::Utf8PathBuf; 11 | use clap::Parser as _; 12 | 13 | use crate::cli; 14 | use ubrn_common::{run_cmd, CrateMetadata}; 15 | 16 | pub fn root_dir() -> Utf8PathBuf { 17 | Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")) 18 | } 19 | 20 | pub fn integration_fixtures_dir() -> Utf8PathBuf { 21 | root_dir().join("../../fixtures") 22 | } 23 | 24 | pub fn fixtures_dir() -> Utf8PathBuf { 25 | root_dir().join("fixtures") 26 | } 27 | 28 | pub fn run_cli(command_line: impl Display) -> Result<()> { 29 | let args = cli::CliArgs::try_parse_from(command_line.to_string().split_whitespace())?; 30 | args.run() 31 | } 32 | 33 | pub fn cargo_build(fixture_name: &str) -> Result { 34 | let mut command = std::process::Command::new("cargo"); 35 | let fixture = integration_fixtures_dir().join(fixture_name); 36 | let manifest_path = fixture.join("Cargo.toml"); 37 | run_cmd( 38 | command 39 | .arg("build") 40 | .arg("--manifest-path") 41 | .arg(&manifest_path), 42 | )?; 43 | CrateMetadata::try_from(manifest_path) 44 | } 45 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/wasm/bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use anyhow::Result; 7 | use camino::Utf8PathBuf; 8 | 9 | use ubrn_bindgen::{BindingsArgs, OutputArgs, SourceArgs, SwitchArgs}; 10 | 11 | use crate::config::ProjectConfig; 12 | 13 | pub(crate) fn bindings( 14 | project: &ProjectConfig, 15 | switches: SwitchArgs, 16 | lib_file: &Utf8PathBuf, 17 | ) -> Result { 18 | let root = project.project_root(); 19 | let config = project.bindings.uniffi_toml_path(root); 20 | if let Some(ref file) = config { 21 | if !file.exists() { 22 | anyhow::bail!("uniffi.toml file {:?} does not exist. Either delete the uniffiToml property or supply a file", file) 23 | } 24 | } 25 | let source = SourceArgs::library(lib_file).with_config(config); 26 | 27 | let ts_dir = project.wasm_bindings_ts_path(root); 28 | let rust_src_dir = project.wasm.crate_dir(root).join("src"); 29 | Ok(BindingsArgs::new( 30 | switches, 31 | source, 32 | OutputArgs::new(&ts_dir, &rust_src_dir, false), 33 | )) 34 | } 35 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/wasm/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | mod bindings; 7 | mod codegen; 8 | mod commands; 9 | mod config; 10 | mod generate; 11 | 12 | pub(crate) use bindings::bindings; 13 | pub(crate) use codegen::get_files; 14 | pub(crate) use commands::WebBuildArgs; 15 | pub(crate) use config::WasmConfig; 16 | pub(crate) use generate::CmdArg; 17 | -------------------------------------------------------------------------------- /crates/ubrn_cli/src/workspace.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use anyhow::Result; 7 | use camino::Utf8PathBuf; 8 | 9 | use crate::config::PackageJson; 10 | 11 | fn find_file_in_parents(file_suffix: &str) -> Result { 12 | let pwd = ubrn_common::pwd()?; 13 | Ok(match ubrn_common::resolve(pwd, file_suffix)? { 14 | Some(file) => file, 15 | _ => anyhow::bail!("{file_suffix} can't be found in current directory"), 16 | }) 17 | } 18 | 19 | pub(crate) fn package_json() -> PackageJson { 20 | let file = find_file_in_parents("package.json").expect("Cannot find package.json"); 21 | ubrn_common::read_from_file(file).expect("Cannot load package.json") 22 | } 23 | 24 | pub(crate) fn project_root() -> Result { 25 | let package_json = find_file_in_parents("package.json")?; 26 | let dir = package_json.parent().expect("Must be a directory"); 27 | Ok(dir.into()) 28 | } 29 | 30 | pub(crate) fn ubrn_config_yaml() -> Result { 31 | find_file_in_parents("ubrn.config.yaml") 32 | } 33 | -------------------------------------------------------------------------------- /crates/ubrn_cli/templates/fixtures/TemplateTester.txt: -------------------------------------------------------------------------------- 1 | hardcoded into template. 2 | module_cpp = {{ self.config.project.module_cpp() }}. 3 | 4 | list of modules = [ 5 | {%- for m in self.config.modules %}' 6 | {{- m.cpp_module() }}' 7 | {%- if !loop.last %}, {% endif -%} 8 | {%- endfor %}] 9 | -------------------------------------------------------------------------------- /crates/ubrn_cli/templates/jsi/android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | {%- let android = self.config.project.android.clone() %} 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /crates/ubrn_cli/templates/jsi/android/ModuleTemplate.kt: -------------------------------------------------------------------------------- 1 | {%- let android = self.config.project.android.clone() %} 2 | {%- let name = self.config.project.module_cpp() %} 3 | {%- let module_class_name = name|fmt("{}Module") -%} 4 | // Generated by uniffi-bindgen-react-native 5 | package {{ android.package_name() }} 6 | 7 | import com.facebook.react.bridge.ReactApplicationContext 8 | import com.facebook.react.module.annotations.ReactModule 9 | import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder 10 | 11 | @ReactModule(name = {{ module_class_name }}.NAME) 12 | class {{ module_class_name }}(reactContext: ReactApplicationContext) : 13 | {{ self.config.project.codegen_filename() }}Spec(reactContext) { 14 | 15 | override fun getName(): String { 16 | return NAME 17 | } 18 | 19 | // Two native methods implemented in cpp-adapter.cpp, and ultimately 20 | // {{ self.config.project.cpp_filename() }}.cpp 21 | 22 | external fun nativeInstallRustCrate(runtimePointer: Long, callInvoker: CallInvokerHolder): Boolean 23 | external fun nativeCleanupRustCrate(runtimePointer: Long): Boolean 24 | 25 | override fun installRustCrate(): Boolean { 26 | val context = this.reactApplicationContext 27 | return nativeInstallRustCrate( 28 | context.javaScriptContextHolder!!.get(), 29 | context.jsCallInvokerHolder!! 30 | ) 31 | } 32 | 33 | override fun cleanupRustCrate(): Boolean { 34 | return nativeCleanupRustCrate( 35 | this.reactApplicationContext.javaScriptContextHolder!!.get() 36 | ) 37 | } 38 | 39 | companion object { 40 | const val NAME = "{{ name }}" 41 | 42 | init { 43 | System.loadLibrary("{{ self.config.project.cpp_filename() }}") 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /crates/ubrn_cli/templates/jsi/android/PackageTemplate.kt: -------------------------------------------------------------------------------- 1 | {%- let name = self.config.project.module_cpp() %} 2 | {%- let package_class_name = name|fmt("{}Package") %} 3 | {%- let module_class_name = name|fmt("{}Module") -%} 4 | // Generated by uniffi-bindgen-react-native 5 | package {{ self.config.project.android.package_name() }} 6 | 7 | import com.facebook.react.TurboReactPackage 8 | import com.facebook.react.bridge.NativeModule 9 | import com.facebook.react.bridge.ReactApplicationContext 10 | import com.facebook.react.module.model.ReactModuleInfo 11 | import com.facebook.react.module.model.ReactModuleInfoProvider 12 | import java.util.HashMap 13 | 14 | class {{ package_class_name }} : TurboReactPackage() { 15 | override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { 16 | return if (name == {{ module_class_name }}.NAME) { 17 | {{ module_class_name }}(reactContext) 18 | } else { 19 | null 20 | } 21 | } 22 | 23 | override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { 24 | return ReactModuleInfoProvider { 25 | val moduleInfos: MutableMap = HashMap() 26 | moduleInfos[{{ module_class_name }}.NAME] = ReactModuleInfo( 27 | {{ module_class_name }}.NAME, 28 | {{ module_class_name }}.NAME, 29 | false, // canOverrideExistingModule 30 | false, // needsEagerInit 31 | false, // isCxxModule 32 | true // isTurboModule 33 | ) 34 | moduleInfos 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /crates/ubrn_cli/templates/jsi/crossplatform/NativeCodegenTemplate.ts: -------------------------------------------------------------------------------- 1 | // Generated by uniffi-bindgen-react-native 2 | import type { TurboModule } from 'react-native'; 3 | import { TurboModuleRegistry } from 'react-native'; 4 | 5 | export interface Spec extends TurboModule { 6 | installRustCrate(): boolean; 7 | cleanupRustCrate(): boolean; 8 | } 9 | 10 | export default TurboModuleRegistry.getEnforcing('{{ self.config.project.spec_name() }}'); 11 | -------------------------------------------------------------------------------- /crates/ubrn_cli/templates/jsi/crossplatform/TurboModuleTemplate.cpp: -------------------------------------------------------------------------------- 1 | // Generated by uniffi-bindgen-react-native 2 | #include "{{ self.config.project.cpp_filename() }}.h" 3 | {%- let root = self.project_root() %} 4 | {%- let bindings = self.config.project.bindings.cpp_path(root) %} 5 | {%- let bindings = self.relative_to(root, bindings) %} 6 | {%- for m in self.config.modules %} 7 | #include "{{ bindings }}/{{ m.hpp_filename() }}" 8 | {%- endfor %} 9 | 10 | namespace {{ self.config.project.cpp_namespace() }} { 11 | using namespace facebook; 12 | 13 | uint8_t installRustCrate(jsi::Runtime &runtime, std::shared_ptr callInvoker) { 14 | {%- for m in self.config.modules %} 15 | {{ m.cpp_module() }}::registerModule(runtime, callInvoker); 16 | {%- endfor %} 17 | return true; 18 | } 19 | 20 | uint8_t cleanupRustCrate(jsi::Runtime &runtime) { 21 | return false; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /crates/ubrn_cli/templates/jsi/crossplatform/TurboModuleTemplate.h: -------------------------------------------------------------------------------- 1 | {%- let ns = self.config.project.cpp_namespace() %} 2 | {%- let marker = ns|upper|fmt("{}_H") -%} 3 | #ifndef {{ marker }} 4 | #define {{ marker }} 5 | // Generated by uniffi-bindgen-react-native 6 | #include 7 | #include 8 | #include 9 | 10 | namespace {{ ns }} { 11 | using namespace facebook; 12 | 13 | uint8_t installRustCrate(jsi::Runtime &runtime, std::shared_ptr callInvoker); 14 | uint8_t cleanupRustCrate(jsi::Runtime &runtime); 15 | } 16 | 17 | #endif /* {{ marker }} */ 18 | -------------------------------------------------------------------------------- /crates/ubrn_cli/templates/jsi/ios/ModuleTemplate.h: -------------------------------------------------------------------------------- 1 | // Generated by uniffi-bindgen-react-native 2 | #ifdef __cplusplus 3 | #import "{{ self.config.project.cpp_filename() }}.h" 4 | #endif 5 | 6 | #ifdef RCT_NEW_ARCH_ENABLED 7 | #import "{{ self.config.project.tm.name() }}.h" 8 | 9 | @interface {{ self.config.project.module_cpp() }} : NSObject <{{ self.config.project.codegen_filename() }}Spec> 10 | #else 11 | #import 12 | 13 | @interface {{ self.config.project.module_cpp() }} : NSObject 14 | #endif 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /crates/ubrn_cli/templates/wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Generated by uniffi-bindgen-react-native 2 | {%- let root = project_root() %} 3 | {%- let crate_dir = config.project.crate_.crate_dir_relative(root) %} 4 | {%- let crate_dir = relative_to(root, crate_dir) %} 5 | [package] 6 | name = "{{ config.project.wasm.wasm_crate_name() }}" 7 | version = "{{ config.project.project_version() }}" 8 | edition = "2018" 9 | 10 | [lib] 11 | crate-type = ["cdylib", "rlib"] 12 | 13 | [features] 14 | default = [] 15 | 16 | [dependencies] 17 | {{ config.rust_crate.package_name() }} = { path = "{{ crate_dir }}" 18 | {%- match config.project.wasm.features %} 19 | {%- when Some(features) %}, features = [ 20 | {%- for feature in features %}"{{ feature }}" 21 | {%- if !loop.last %}, {% endif %} 22 | {%- endfor -%} 23 | ] 24 | {%- else %} 25 | {%- endmatch %} 26 | {%- match config.project.wasm.default_features %} 27 | {%- when Some(flag) %}, default-features = {{ flag }} 28 | {%- else %} 29 | {%- endmatch %} } 30 | # We want to ensure that the version of wasm-bindgen is selected by the 31 | # uniffi-runtime-javascript crate. 32 | # cargo is smart enough to do this if we don't put any further restrictions 33 | # on it. 34 | uniffi-runtime-javascript = { version = "{{ runtime_version() }}", features = ["wasm32"] } 35 | wasm-bindgen = "*" 36 | 37 | [profile.release] 38 | # Tell `rustc` to optimize for small code size. 39 | opt-level = "s" 40 | 41 | {%- if !config.project.wasm.workspace %} 42 | 43 | [workspace] 44 | {%- endif %} 45 | {# space -#} 46 | -------------------------------------------------------------------------------- /crates/ubrn_cli/templates/wasm/index.web.ts: -------------------------------------------------------------------------------- 1 | // Generated by uniffi-bindgen-react-native 2 | // Export the generated bindings to the app. 3 | {%- let root = self.project_root() %} 4 | {%- let bindings = self.config.project.wasm_bindings_ts_path(root) %} 5 | {%- let bindings = self.relative_to(root, bindings) %} 6 | {%- for m in self.config.modules %} 7 | export * from './{{ bindings }}/{{ m.ts() }}'; 8 | {%- endfor %} 9 | 10 | // Now import the bindings so we can: 11 | // - intialize them 12 | // - export them as namespaced objects as the default export. 13 | {%- for m in self.config.modules %} 14 | import * as {{ m.ts() }} from './{{ bindings }}/{{ m.ts() }}'; 15 | {%- endfor %} 16 | 17 | import initAsync from './{{ bindings }}/wasm-bindgen/index.js'; 18 | import wasmPath from './{{ bindings }}/wasm-bindgen/index_bg.wasm'; 19 | 20 | export async function uniffiInitAsync() { 21 | await initAsync({ module_or_path: wasmPath }) 22 | 23 | // Initialize the generated bindings: mostly checksums, but also callbacks. 24 | // - the boolean flag ensures this loads exactly once, even if the JS code 25 | // is reloaded (e.g. during development with metro). 26 | {%- for m in self.config.modules %} 27 | {{ m.ts() }}.default.initialize(); 28 | {%- endfor %} 29 | } 30 | 31 | // Export the crates as individually namespaced objects. 32 | export default { 33 | {%- for m in self.config.modules %} 34 | {{ m.ts() }}, 35 | {%- endfor %} 36 | }; 37 | 38 | {# space #} 39 | -------------------------------------------------------------------------------- /crates/ubrn_cli/templates/wasm/lib.rs: -------------------------------------------------------------------------------- 1 | // Generated by uniffi-bindgen-react-native 2 | 3 | {{ entrypoint() }} 4 | 5 | // end. 6 | -------------------------------------------------------------------------------- /crates/ubrn_cli/tests/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | mod happy_path; 8 | mod web_variants; 9 | -------------------------------------------------------------------------------- /crates/ubrn_cli_testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ubrn_cli_testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MPL-2.0" 6 | description = "Testing utilities for UBRN CLI tools" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | anyhow = "1.0" 12 | camino = { workspace = true } 13 | ubrn_common = { path = "../ubrn_common" } 14 | 15 | [dev-dependencies] 16 | tempfile = "3.8" 17 | -------------------------------------------------------------------------------- /crates/ubrn_cli_testing/src/cli.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use std::process::Command as StdCommand; 7 | 8 | use anyhow::{Context, Result}; 9 | 10 | /// Run a CLI command and return the result. 11 | /// 12 | /// This function takes a command string, splits it into a program and arguments, 13 | /// and runs it. When recording is enabled, the command will be recorded but not executed. 14 | /// 15 | /// # Arguments 16 | /// 17 | /// * `cmd_str` - The command string to run (e.g., "build ios --config config.yaml") 18 | /// 19 | /// # Returns 20 | /// 21 | /// Returns a `Result` indicating success or failure 22 | /// 23 | /// # Example 24 | /// 25 | /// ```no_run 26 | /// use ubrn_cli_testing::run_cli; 27 | /// 28 | /// fn test_cli_command() -> anyhow::Result<()> { 29 | /// run_cli("build ios --and-generate --config ubrn.config.yaml")?; 30 | /// Ok(()) 31 | /// } 32 | /// ``` 33 | pub fn run_cli(cmd_str: &str) -> Result<()> { 34 | let parts: Vec<&str> = cmd_str.split_whitespace().collect(); 35 | 36 | if parts.is_empty() { 37 | return Err(anyhow::anyhow!("Empty command string")); 38 | } 39 | 40 | let program = parts[0]; 41 | let args = &parts[1..]; 42 | let mut cmd = StdCommand::new(program); 43 | cmd.args(args); 44 | 45 | // Use the ubrn_common command execution, which handles recording 46 | ubrn_common::run_cmd(&mut cmd) 47 | .with_context(|| format!("Failed to execute command: {}", cmd_str)) 48 | } 49 | -------------------------------------------------------------------------------- /crates/ubrn_cli_testing/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | mod cli; 7 | mod command; 8 | mod command_assertions; 9 | mod file; 10 | mod file_assertions; 11 | mod fixture; 12 | mod recording; 13 | 14 | #[cfg(test)] 15 | mod tests; 16 | 17 | // Re-export modules 18 | pub use cli::run_cli; 19 | pub use command::Command; 20 | pub use command_assertions::{assert_commands, commands_match}; 21 | pub use file::File; 22 | pub use file_assertions::{assert_files, files_match}; 23 | pub use fixture::with_fixture; 24 | pub use recording::{start_recording, stop_recording}; 25 | pub use ubrn_common::{shim_file_str, shim_path}; 26 | -------------------------------------------------------------------------------- /crates/ubrn_cli_testing/src/recording.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | /// Start recording commands (commands will be recorded but not executed) 8 | pub fn start_recording() { 9 | ubrn_common::enable_command_recording(); 10 | ubrn_common::clear_recorded_commands(); 11 | ubrn_common::clear_recorded_files(); 12 | ubrn_common::clear_file_shims(); 13 | } 14 | 15 | /// Stop recording commands and clear the recorded commands 16 | pub fn stop_recording() { 17 | ubrn_common::disable_command_recording(); 18 | ubrn_common::clear_recorded_commands(); 19 | ubrn_common::clear_recorded_files(); 20 | ubrn_common::clear_file_shims(); 21 | } 22 | -------------------------------------------------------------------------------- /crates/ubrn_cli_testing/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | mod command_tests; 7 | mod file_shim_str_tests; 8 | mod file_shim_tests; 9 | mod file_tests; 10 | mod fixture_tests; 11 | -------------------------------------------------------------------------------- /crates/ubrn_common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ubrn_common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | anyhow = { workspace = true } 9 | camino = { workspace = true } 10 | cargo_metadata = { workspace = true } 11 | extend = { workspace = true } 12 | glob = "0.3.1" 13 | serde = { workspace = true } 14 | serde_json = "1.0.117" 15 | serde_yaml = "0.9.34" 16 | toml = { workspace = true } 17 | which = "6.0.1" 18 | -------------------------------------------------------------------------------- /crates/ubrn_common/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | mod commands; 7 | mod files; 8 | pub mod fmt; 9 | mod rust_crate; 10 | mod serde; 11 | mod testing; 12 | 13 | pub use commands::*; 14 | pub use files::*; 15 | pub use rust_crate::*; 16 | pub use serde::*; 17 | pub use testing::*; 18 | -------------------------------------------------------------------------------- /crates/ubrn_common/src/serde.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use serde::de::DeserializeOwned; 8 | 9 | /// Convenience method to implement Default, but in terms of the `serde(default_value)` rather 10 | /// than `derive(Default)`. 11 | pub fn default() -> T 12 | where 13 | T: DeserializeOwned, 14 | { 15 | let empty = serde_json::Value::Object(Default::default()); 16 | serde_json::from_value(empty).unwrap_or_else(|e| panic!("Failed to create default: {e:?}")) 17 | } 18 | -------------------------------------------------------------------------------- /crates/ubrn_testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ubrn_testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [target.'cfg(target_arch = "wasm32")'.dependencies] 8 | wasm-bindgen-futures = "0.4" 9 | gloo-timers = { version = "0.3.0", features = ["futures"] } 10 | 11 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 12 | tokio = { version = "1.24.1", features = ["time", "sync"] } 13 | -------------------------------------------------------------------------------- /crates/ubrn_testing/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | pub mod thread; 7 | pub mod timer; 8 | -------------------------------------------------------------------------------- /crates/ubrn_testing/src/thread.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | mod platform { 8 | #[cfg(target_arch = "wasm32")] 9 | mod wasm { 10 | use wasm_bindgen_futures::spawn_local; 11 | 12 | pub fn spawn_task(f: F) 13 | where 14 | F: FnOnce() -> (), 15 | F: Send + 'static, 16 | { 17 | spawn_local(async move { 18 | f(); 19 | }); 20 | } 21 | } 22 | 23 | #[cfg(not(target_arch = "wasm32"))] 24 | mod native { 25 | use std::thread; 26 | 27 | pub type JoinHandle = thread::JoinHandle; 28 | 29 | // pub fn spawn_task(f: impl FnOnce() + Send + 'static) -> JoinHandle<()> { 30 | pub fn spawn_task(f: F) -> JoinHandle 31 | where 32 | F: FnOnce() -> T, 33 | F: Send + 'static, 34 | T: Send + 'static, 35 | { 36 | thread::spawn(f) 37 | } 38 | } 39 | 40 | #[cfg(not(target_arch = "wasm32"))] 41 | pub use native::*; 42 | #[cfg(target_arch = "wasm32")] 43 | pub use wasm::*; 44 | } 45 | 46 | pub use self::platform::*; 47 | -------------------------------------------------------------------------------- /crates/ubrn_testing/src/timer/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use std::{future::Future, time::Duration}; 7 | 8 | pub struct TimerFuture; 9 | pub trait TimerService { 10 | type Future: Future; 11 | fn sleep(duration: Duration) -> Self::Future; 12 | } 13 | 14 | #[cfg(not(target_arch = "wasm32"))] 15 | mod native; 16 | #[cfg(not(target_arch = "wasm32"))] 17 | pub use native::*; 18 | 19 | #[cfg(target_arch = "wasm32")] 20 | mod wasm; 21 | #[allow(unused)] 22 | #[cfg(target_arch = "wasm32")] 23 | pub(crate) use wasm::*; 24 | -------------------------------------------------------------------------------- /crates/ubrn_testing/src/timer/wasm.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use std::time::Duration; 7 | 8 | use gloo_timers::future::TimeoutFuture; 9 | 10 | use super::{TimerFuture, TimerService}; 11 | 12 | impl TimerService for TimerFuture { 13 | type Future = TimeoutFuture; 14 | fn sleep(duration: Duration) -> Self::Future { 15 | let millis = duration.as_millis(); 16 | let millis = millis.try_into().unwrap(); 17 | TimeoutFuture::new(millis) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /crates/uniffi-runtime-javascript/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-runtime-javascript" 3 | version = "0.29.0-0.1" 4 | edition = "2021" 5 | authors = ["James Hugman "] 6 | description = "Javascript runtime for UniFFI-generated bindings" 7 | license = "MPL-2.0" 8 | repository = "https://github.com/jhugman/uniffi-bindgen-react-native" 9 | readme = "README.md" 10 | keywords = ["ffi", "code-generation", "javascript", "wasm"] 11 | categories = ["api-bindings", "development-tools::ffi"] 12 | 13 | [dependencies] 14 | uniffi_core = { version = "0.29", default-features = false } 15 | wasm-bindgen = { version = "0.2.97", optional = true } 16 | 17 | [features] 18 | wasm32 = ["wasm-bindgen"] 19 | -------------------------------------------------------------------------------- /crates/uniffi-runtime-javascript/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | #[cfg(feature = "wasm32")] 7 | mod wasm32; 8 | 9 | #[cfg(feature = "wasm32")] 10 | pub use wasm32::*; 11 | -------------------------------------------------------------------------------- /docs/src/README.md: -------------------------------------------------------------------------------- 1 | [`uniffi-rs`](https://github.com/mozilla/uniffi-rs/blob/main/README.md) is a suite of projects to allow Rust to be used from other languages. It was started at Mozilla to facilitate building cross-platform components in Rust which could be run on Android and iOS. 2 | 3 | It has since grown to support for other languages not in use at Mozilla. 4 | 5 | ![React Native Logo](images/react-native-logo.svg) 6 | + 7 | ![Rust Logo](images/rust-logo.svg) 8 | 9 | [`uniffi-bindgen-react-native`](https://github.com/jhugman/uniffi-bindgen-react-native) is the project that houses the bindings generators for react-native. 10 | 11 | It supports all language features that `uniffi-rs` supports, including: 12 | 13 | - calling functions from Typescript to Rust, synchronous and asynchronous. 14 | - calling functions from Rust to Typescript, synchronous and asynchronous. 15 | - objects with methods, including: 16 | - garbage collection integration. 17 | - uniffi traits 18 | - custom types 19 | 20 | It contains tooling to generate bindings from Hermes via JSI, and to generate the code to create turbo-modules. 21 | 22 | ```admonish warning 23 | This project is still in early development, and should not yet be used in production. 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/src/idioms/types.md: -------------------------------------------------------------------------------- 1 | # Types 2 | -------------------------------------------------------------------------------- /docs/src/images/react-native-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/src/reference/reserved-words.md: -------------------------------------------------------------------------------- 1 | # Reserved words 2 | 3 | ## Typescript Reserved Words 4 | 5 | The following words are reserved words in Typescript. 6 | 7 | If the Rust API uses any of these words on their own, the generated typescript is appended with an underscore (`_`). 8 | 9 | Reserved Words | Strict Mode Reserved Words 10 | ---------------| --------------------------- 11 | `break` | `as` 12 | `case` | `implements` 13 | `catch` | `interface` 14 | `class` | `let` 15 | `const` | `package` 16 | `continue` | `private` 17 | `debugger` | `protected` 18 | `default` | `public` 19 | `delete` | `static` 20 | `do` | `yield` 21 | `else` 22 | `enum` 23 | `export` 24 | `extends` 25 | `false` 26 | `finally` 27 | `for` 28 | `function` 29 | `if` 30 | `import` 31 | `in` 32 | `instanceof` 33 | `new` 34 | `null` 35 | `return` 36 | `super` 37 | `switch` 38 | `this` 39 | `throw` 40 | `true` 41 | `try` 42 | `typeof` 43 | `var` 44 | `void` 45 | `while` 46 | `with` 47 | 48 | e.g. 49 | 50 | ```rust 51 | #[uniffi::export] 52 | fn void() {} 53 | ``` 54 | 55 | generates valid Typescript: 56 | 57 | ```typescript 58 | function void_() { 59 | // … call into Rust. 60 | } 61 | ``` 62 | 63 | ## `Error` is mapped to `Exception` 64 | 65 | Due to the relative prevalence in idiomatic Rust of an error enum called `Error`, and the built-in `Error` class in Javascript, an `Error` enum is renamed to `Exception`. 66 | 67 | ## Uniffi Reserved words 68 | 69 | In your Rust code, avoid using identifiers beginning with the word `uniffi` or `Uniffi`. 70 | -------------------------------------------------------------------------------- /fixtures/arithmetic-procmacro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-example-arithmetic-procmacro" 3 | edition = "2021" 4 | version = "0.22.0" 5 | license = "MPL-2.0" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["lib", "cdylib"] 10 | name = "arithmeticpm" 11 | 12 | [dependencies] 13 | uniffi = { workspace = true } 14 | thiserror = "1.0" 15 | 16 | [dev-dependencies] 17 | uniffi = { workspace = true, features = ["bindgen-tests"] } 18 | -------------------------------------------------------------------------------- /fixtures/arithmetic-procmacro/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | #[derive(Debug, thiserror::Error, uniffi::Error)] 8 | pub enum ArithmeticError { 9 | #[error("Integer overflow on an operation with {a} and {b}")] 10 | IntegerOverflow { a: u64, b: u64 }, 11 | } 12 | 13 | #[uniffi::export] 14 | fn add(a: u64, b: u64) -> Result { 15 | a.checked_add(b) 16 | .ok_or(ArithmeticError::IntegerOverflow { a, b }) 17 | } 18 | 19 | #[uniffi::export] 20 | fn sub(a: u64, b: u64) -> Result { 21 | a.checked_sub(b) 22 | .ok_or(ArithmeticError::IntegerOverflow { a, b }) 23 | } 24 | 25 | #[uniffi::export] 26 | fn div(dividend: u64, divisor: u64) -> u64 { 27 | if divisor == 0 { 28 | panic!("Can't divide by zero"); 29 | } 30 | dividend / divisor 31 | } 32 | 33 | #[uniffi::export] 34 | fn equal(a: u64, b: u64) -> bool { 35 | a == b 36 | } 37 | 38 | type Result = std::result::Result; 39 | 40 | uniffi::setup_scaffolding!(); 41 | -------------------------------------------------------------------------------- /fixtures/arithmetic-procmacro/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/arithmetic-procmacro/tests/bindings/test_arithmetic_procmacro.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | // fixture=arithmetic 7 | // cargo run --manifest-path ./crates/ubrn_cli/Cargo.toml -- ./fixtures/${fixture}/src/${fixture}.udl --out-dir ./fixtures/${fixture}/generated 8 | // cargo xtask run ./fixtures/${fixture}/tests/bindings/test_${fixture}.ts --cpp ./fixtures/${fixture}/generated/${fixture}.cpp --crate ./fixtures/${fixture} 9 | 10 | import * as rust from "../../generated/arithmeticpm"; 11 | import { test } from "@/asserts"; 12 | import "@/polyfills"; 13 | 14 | const a = BigInt(39); 15 | const b = BigInt(3); 16 | 17 | test("add", (t) => { 18 | console.log(`${a} + ${b} = ${rust.add(a, b)}`); 19 | t.assertEqual(a + b, rust.add(a, b)); 20 | }); 21 | test("sub", (t) => { 22 | t.assertEqual(a - b, rust.sub(a, b)); 23 | }); 24 | test("div", (t) => { 25 | t.assertEqual(a / b, rust.div(a, b)); 26 | }); 27 | -------------------------------------------------------------------------------- /fixtures/arithmetic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-example-arithmetic" 3 | edition = "2021" 4 | version = "0.22.0" 5 | authors = ["Firefox Sync Team "] 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["lib", "staticlib", "cdylib"] 11 | name = "arithmetical" 12 | 13 | [dependencies] 14 | uniffi = { workspace = true } 15 | thiserror = "1.0" 16 | 17 | [build-dependencies] 18 | uniffi = { workspace = true, features = ["build"] } 19 | 20 | [dev-dependencies] 21 | uniffi = { workspace = true, features = ["bindgen-tests"] } 22 | -------------------------------------------------------------------------------- /fixtures/arithmetic/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("src/arithmetic.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/arithmetic/src/arithmetic.udl: -------------------------------------------------------------------------------- 1 | [Error] 2 | enum ArithmeticError { 3 | "IntegerOverflow", 4 | }; 5 | 6 | namespace arithmetic { 7 | [Throws=ArithmeticError] 8 | u64 add(u64 a, u64 b); 9 | 10 | [Throws=ArithmeticError] 11 | u64 sub(u64 a, u64 b); 12 | 13 | u64 div(u64 dividend, u64 divisor); 14 | 15 | boolean equal(u64 a, u64 b); 16 | }; 17 | -------------------------------------------------------------------------------- /fixtures/arithmetic/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | #[derive(Debug, thiserror::Error)] 8 | pub enum ArithmeticError { 9 | #[error("Integer overflow on an operation with {a} and {b}")] 10 | IntegerOverflow { a: u64, b: u64 }, 11 | } 12 | 13 | fn add(a: u64, b: u64) -> Result { 14 | a.checked_add(b) 15 | .ok_or(ArithmeticError::IntegerOverflow { a, b }) 16 | } 17 | 18 | fn sub(a: u64, b: u64) -> Result { 19 | a.checked_sub(b) 20 | .ok_or(ArithmeticError::IntegerOverflow { a, b }) 21 | } 22 | 23 | fn div(dividend: u64, divisor: u64) -> u64 { 24 | if divisor == 0 { 25 | panic!("Can't divide by zero"); 26 | } 27 | dividend / divisor 28 | } 29 | 30 | fn equal(a: u64, b: u64) -> bool { 31 | a == b 32 | } 33 | 34 | type Result = std::result::Result; 35 | 36 | uniffi::include_scaffolding!("arithmetic"); 37 | -------------------------------------------------------------------------------- /fixtures/arithmetic/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/arithmetic/tests/bindings/test_arithmetic.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | // fixture=arithmetic 7 | // cargo run --manifest-path ./crates/ubrn_cli/Cargo.toml -- ./fixtures/${fixture}/src/${fixture}.udl --out-dir ./fixtures/${fixture}/generated 8 | // cargo xtask run ./fixtures/${fixture}/tests/bindings/test_${fixture}.ts --cpp ./fixtures/${fixture}/generated/${fixture}.cpp --crate ./fixtures/${fixture} 9 | 10 | import * as rust from "../../generated/arithmetic"; 11 | import { test } from "@/asserts"; 12 | import "@/polyfills"; 13 | 14 | const a = BigInt(39); 15 | const b = BigInt(3); 16 | 17 | test("add", (t) => { 18 | console.log(`${a} + ${b} = ${rust.add(a, b)}`); 19 | t.assertEqual(a + b, rust.add(a, b)); 20 | }); 21 | test("sub", (t) => { 22 | t.assertEqual(a - b, rust.sub(a, b)); 23 | }); 24 | test("div", (t) => { 25 | t.assertEqual(a / b, rust.div(a, b)); 26 | }); 27 | -------------------------------------------------------------------------------- /fixtures/async-callbacks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixtures-async-callbacks" 3 | version = "0.21.0" 4 | authors = ["jhugman"] 5 | edition = "2021" 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | name = "async_callbacks" 11 | crate-type = ["lib", "cdylib"] 12 | 13 | [dependencies] 14 | uniffi = { workspace = true, features = ["tokio", "cli"] } 15 | async-trait = "0.1" 16 | futures = "0.3" 17 | thiserror = "1.0" 18 | tokio = { version = "1.24.1", features = ["time", "sync"] } 19 | once_cell = "1.18.0" 20 | ubrn_testing = { path = "../../crates/ubrn_testing" } 21 | 22 | [build-dependencies] 23 | uniffi = { workspace = true, features = ["build"] } 24 | 25 | [dev-dependencies] 26 | uniffi = { workspace = true, features = ["bindgen-tests"] } 27 | -------------------------------------------------------------------------------- /fixtures/async-callbacks/README.md: -------------------------------------------------------------------------------- 1 | # A basic test for uniffi components 2 | 3 | This test covers async functions and methods. It also provides examples. 4 | 5 | ## Run the tests 6 | 7 | Simply use `cargo`: 8 | 9 | ```sh 10 | $ cargo test 11 | ``` 12 | 13 | It is possible to filter by test names, like `cargo test -- swift` to only run 14 | Swift's tests. 15 | 16 | ## Run the examples 17 | 18 | At the time of writing, each `examples/*` directory has a `Makefile`. They are 19 | mostly designed for Unix-ish systems, sorry for that. 20 | 21 | To run the examples, first `uniffi` must be compiled: 22 | 23 | ```sh 24 | $ cargo build --release -p uniffi` 25 | ``` 26 | 27 | Then, each `Makefile` has 2 targets: `build` and `run`: 28 | 29 | ```sh 30 | $ # Build the examples. 31 | $ make build 32 | $ 33 | $ # Run the example. 34 | $ make run 35 | ``` 36 | 37 | One note for `examples/kotlin/`, some JAR files must be present, so please 38 | run `make install-jar` first: It will just download the appropriated JAR files 39 | directly inside the directory from Maven. -------------------------------------------------------------------------------- /fixtures/async-callbacks/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | jsi 2 | wasm 3 | -------------------------------------------------------------------------------- /fixtures/async-callbacks/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/async-callbacks/uniffi.toml: -------------------------------------------------------------------------------- 1 | [bindings.typescript] 2 | logLevel = "debug" 3 | consoleImport = "@/hermes" 4 | -------------------------------------------------------------------------------- /fixtures/async-calls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixtures-async-calls" 3 | version = "0.21.0" 4 | authors = ["jhugman"] 5 | edition = "2021" 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | name = "uniffi_async_calls" 11 | crate-type = ["lib", "cdylib"] 12 | 13 | [[bin]] 14 | name = "uniffi-fixtures-async-calls" 15 | path = "src/bin.rs" 16 | 17 | [dependencies] 18 | async-trait = "0.1" 19 | futures = "0.3" 20 | thiserror = "1.0" 21 | once_cell = "1.18.0" 22 | ubrn_testing = { path = "../../crates/ubrn_testing" } 23 | uniffi = { workspace = true, features = ["tokio", "cli"] } 24 | 25 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 26 | tokio = { version = "1.24.1", features = ["time", "sync"] } 27 | 28 | [build-dependencies] 29 | uniffi = { workspace = true, features = ["build"] } 30 | 31 | [dev-dependencies] 32 | uniffi = { workspace = true, features = ["bindgen-tests"] } 33 | -------------------------------------------------------------------------------- /fixtures/async-calls/README.md: -------------------------------------------------------------------------------- 1 | # A basic test for uniffi components 2 | 3 | This test covers async functions and methods. It also provides examples. 4 | 5 | ## Run the tests 6 | 7 | Simply use `cargo`: 8 | 9 | ```sh 10 | $ cargo test 11 | ``` 12 | 13 | It is possible to filter by test names, like `cargo test -- swift` to only run 14 | Swift's tests. 15 | 16 | ## Run the examples 17 | 18 | At the time of writing, each `examples/*` directory has a `Makefile`. They are 19 | mostly designed for Unix-ish systems, sorry for that. 20 | 21 | To run the examples, first `uniffi` must be compiled: 22 | 23 | ```sh 24 | $ cargo build --release -p uniffi` 25 | ``` 26 | 27 | Then, each `Makefile` has 2 targets: `build` and `run`: 28 | 29 | ```sh 30 | $ # Build the examples. 31 | $ make build 32 | $ 33 | $ # Run the example. 34 | $ make run 35 | ``` 36 | 37 | One note for `examples/kotlin/`, some JAR files must be present, so please 38 | run `make install-jar` first: It will just download the appropriated JAR files 39 | directly inside the directory from Maven. -------------------------------------------------------------------------------- /fixtures/async-calls/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | fn main() { 7 | uniffi::generate_scaffolding("src/async-calls.udl").unwrap(); 8 | } 9 | -------------------------------------------------------------------------------- /fixtures/async-calls/src/async-calls.udl: -------------------------------------------------------------------------------- 1 | namespace futures { 2 | [Async] 3 | boolean always_ready(); 4 | }; 5 | 6 | interface UdlMegaphone { 7 | [Async] 8 | constructor(); 9 | 10 | [Async, Name="secondary"] 11 | constructor(); 12 | 13 | [Async] 14 | string say_after(u16 ms, string who); 15 | }; 16 | -------------------------------------------------------------------------------- /fixtures/async-calls/src/bin.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | fn main() { 7 | uniffi::uniffi_bindgen_main() 8 | } 9 | -------------------------------------------------------------------------------- /fixtures/async-calls/src/platform_specific/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | #[cfg(not(target_arch = "wasm32"))] 8 | mod native; 9 | #[cfg(not(target_arch = "wasm32"))] 10 | pub(crate) use native::*; 11 | 12 | #[cfg(target_arch = "wasm32")] 13 | mod wasm; 14 | #[cfg(target_arch = "wasm32")] 15 | pub(crate) use wasm::*; 16 | -------------------------------------------------------------------------------- /fixtures/async-calls/src/platform_specific/native.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use std::time::Duration; 7 | 8 | use crate::{AsyncError, SharedResourceOptions}; 9 | 10 | pub(crate) async fn acquire_with_timeout(options: SharedResourceOptions) -> Result<(), AsyncError> { 11 | use once_cell::sync::Lazy; 12 | use tokio::{ 13 | sync::Mutex, 14 | time::{sleep, timeout}, 15 | }; 16 | 17 | static MUTEX: Lazy> = Lazy::new(|| Mutex::new(())); 18 | 19 | let _guard = timeout( 20 | Duration::from_millis(options.timeout_ms.into()), 21 | MUTEX.lock(), 22 | ) 23 | .await 24 | .map_err(|_| { 25 | println!("Timeout error in use_shared_resource(). The unit test may hang after this"); 26 | AsyncError::Timeout 27 | })?; 28 | 29 | sleep(Duration::from_millis(options.release_after_ms.into())).await; 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /fixtures/async-calls/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | jsi 2 | wasm 3 | -------------------------------------------------------------------------------- /fixtures/async-calls/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/async-calls/uniffi.toml: -------------------------------------------------------------------------------- 1 | [bindings.typescript] 2 | logLevel = "debug" 3 | consoleImport = "@/hermes" 4 | -------------------------------------------------------------------------------- /fixtures/async-traits/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixtures-async-traits" 3 | version = "0.21.0" 4 | authors = ["jhugman"] 5 | edition = "2021" 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | name = "async_traits" 11 | crate-type = ["lib", "cdylib"] 12 | 13 | [dependencies] 14 | uniffi = { workspace = true, features = ["tokio", "cli"] } 15 | async-trait = "0.1" 16 | futures = "0.3" 17 | thiserror = "1.0" 18 | tokio = { version = "1.24.1", features = ["time", "sync"] } 19 | once_cell = "1.18.0" 20 | ubrn_testing = { path = "../../crates/ubrn_testing" } 21 | 22 | [build-dependencies] 23 | uniffi = { workspace = true, features = ["build"] } 24 | 25 | [dev-dependencies] 26 | uniffi = { workspace = true, features = ["bindgen-tests"] } 27 | -------------------------------------------------------------------------------- /fixtures/async-traits/README.md: -------------------------------------------------------------------------------- 1 | # A basic test for uniffi components 2 | 3 | This test covers async functions and methods. It also provides examples. 4 | 5 | ## Run the tests 6 | 7 | Simply use `cargo`: 8 | 9 | ```sh 10 | $ cargo test 11 | ``` 12 | 13 | It is possible to filter by test names, like `cargo test -- swift` to only run 14 | Swift's tests. 15 | 16 | ## Run the examples 17 | 18 | At the time of writing, each `examples/*` directory has a `Makefile`. They are 19 | mostly designed for Unix-ish systems, sorry for that. 20 | 21 | To run the examples, first `uniffi` must be compiled: 22 | 23 | ```sh 24 | $ cargo build --release -p uniffi` 25 | ``` 26 | 27 | Then, each `Makefile` has 2 targets: `build` and `run`: 28 | 29 | ```sh 30 | $ # Build the examples. 31 | $ make build 32 | $ 33 | $ # Run the example. 34 | $ make run 35 | ``` 36 | 37 | One note for `examples/kotlin/`, some JAR files must be present, so please 38 | run `make install-jar` first: It will just download the appropriated JAR files 39 | directly inside the directory from Maven. -------------------------------------------------------------------------------- /fixtures/async-traits/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | jsi 2 | wasm 3 | -------------------------------------------------------------------------------- /fixtures/async-traits/tests/bindings/test_async_traits.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | import myModule, { getSayAfterTraits } from "../../generated/async_traits"; 8 | import { asyncTest, Asserts, test } from "@/asserts"; 9 | import { 10 | uniffiRustFutureHandleCount, 11 | uniffiForeignFutureHandleCount, 12 | } from "uniffi-bindgen-react-native"; 13 | import "@/polyfills"; 14 | 15 | // Initialize the callbacks for the module. 16 | // This will be hidden in the installation process. 17 | myModule.initialize(); 18 | 19 | function checkRemainingFutures(t: Asserts) { 20 | t.assertEqual( 21 | 0, 22 | uniffiRustFutureHandleCount(), 23 | "Number of remaining futures should be zero", 24 | ); 25 | t.assertEqual( 26 | 0, 27 | uniffiForeignFutureHandleCount(), 28 | "Number of remaining foreign futures should be zero", 29 | ); 30 | } 31 | 32 | (async () => { 33 | await asyncTest("Async trait interface methods", async (t) => { 34 | const traits = getSayAfterTraits(); 35 | 36 | await t.asyncMeasure( 37 | async () => { 38 | let result1 = await traits[0].sayAfter(300, "Alice"); 39 | let result2 = await traits[1].sayAfter(200, "Bob"); 40 | 41 | t.assertEqual(result1, "Hello, Alice!"); 42 | t.assertEqual(result2, "Hello, Bob!"); 43 | }, 44 | 500, 45 | 100, 46 | ); 47 | checkRemainingFutures(t); 48 | t.end(); 49 | }); 50 | })(); 51 | -------------------------------------------------------------------------------- /fixtures/async-traits/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/async-traits/uniffi.toml: -------------------------------------------------------------------------------- 1 | [bindings.typescript] 2 | logLevel = "debug" 3 | consoleImport = "@/hermes" 4 | -------------------------------------------------------------------------------- /fixtures/callbacks-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-example-callbacks" 3 | edition = "2021" 4 | version = "0.22.0" 5 | license = "MPL-2.0" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["lib", "cdylib"] 10 | name = "callbacks" 11 | 12 | [dependencies] 13 | uniffi = { workspace = true } 14 | thiserror = "1.0" 15 | 16 | [build-dependencies] 17 | uniffi = { workspace = true, features = ["build"] } 18 | 19 | [dev-dependencies] 20 | uniffi = { workspace = true, features = ["bindgen-tests"] } 21 | -------------------------------------------------------------------------------- /fixtures/callbacks-example/README.md: -------------------------------------------------------------------------------- 1 | This is an example of UniFFI "callbacks". 2 | 3 | Callbacks are the ability to implement Rust traits in foreign languages. These are defined by 4 | [normal UniFFI traits](../../docs/manual/src/foreign_traits.md) or via ["callback" definitions](../../docs/manual/src/udl/callback_interfaces.md). 5 | 6 | -------------------------------------------------------------------------------- /fixtures/callbacks-example/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("src/callbacks.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/callbacks-example/src/callbacks.udl: -------------------------------------------------------------------------------- 1 | namespace callbacks { 2 | // `get_sim_cards` is defined via a procmacro. 3 | }; 4 | 5 | // This trait is implemented in Rust and in foreign bindings. 6 | [Trait, WithForeign] 7 | interface SimCard { 8 | string name(); // The name of the carrier/provider. 9 | }; 10 | 11 | [Error] 12 | enum TelephoneError { 13 | "Busy", 14 | "InternalTelephoneError", 15 | }; 16 | 17 | interface Telephone { 18 | constructor(); 19 | [Throws=TelephoneError] 20 | string call(SimCard sim, CallAnswerer answerer); 21 | }; 22 | 23 | // callback interfaces are discouraged now that foreign code can 24 | // implement traits, but here's one! 25 | callback interface CallAnswerer { 26 | [Throws=TelephoneError] 27 | string answer(); 28 | }; 29 | -------------------------------------------------------------------------------- /fixtures/callbacks-example/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | -------------------------------------------------------------------------------- /fixtures/callbacks-example/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/callbacks-example/uniffi.toml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhugman/uniffi-bindgen-react-native/2de076e95efafcfa26350eb51ace6cc22f4aff84/fixtures/callbacks-example/uniffi.toml -------------------------------------------------------------------------------- /fixtures/callbacks-regression/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-example-callbacks-deadlock-regression" 3 | edition = "2021" 4 | version = "0.22.0" 5 | license = "MPL-2.0" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["lib", "cdylib"] 10 | name = "uniffi_callbacks" 11 | 12 | [dependencies] 13 | uniffi = { workspace = true } 14 | ubrn_testing = { path = "../../crates/ubrn_testing" } 15 | thiserror = "1.0" 16 | 17 | [target.'cfg(target_arch = "wasm32")'.dependencies] 18 | wasm-bindgen-futures = "0.4" 19 | 20 | [build-dependencies] 21 | uniffi = { workspace = true, features = ["build"] } 22 | 23 | [dev-dependencies] 24 | uniffi = { workspace = true, features = ["bindgen-tests"] } 25 | -------------------------------------------------------------------------------- /fixtures/callbacks-regression/README.md: -------------------------------------------------------------------------------- 1 | This is an example of UniFFI "callbacks". 2 | 3 | Callbacks are the ability to implement Rust traits in foreign languages. These are defined by 4 | [normal UniFFI traits](../../docs/manual/src/foreign_traits.md) or via ["callback" definitions](../../docs/manual/src/udl/callback_interfaces.md). 5 | 6 | -------------------------------------------------------------------------------- /fixtures/callbacks-regression/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /fixtures/callbacks-regression/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use std::sync::Arc; 8 | use ubrn_testing::thread::spawn_task; 9 | 10 | uniffi::setup_scaffolding!(); 11 | 12 | #[uniffi::export(with_foreign)] 13 | pub trait EventListener: Send + Sync { 14 | fn on_event(&self, message: String, number: i32); 15 | } 16 | 17 | #[derive(uniffi::Object)] 18 | struct EventSource { 19 | listener: Arc, 20 | } 21 | 22 | #[uniffi::export] 23 | impl EventSource { 24 | #[uniffi::constructor] 25 | fn new(listener: Arc) -> Arc { 26 | Arc::new(Self { listener }) 27 | } 28 | fn start(self: Arc, message: String, until: i32) { 29 | let listener = self.listener.clone(); 30 | spawn_task(move || { 31 | for i in 0..until { 32 | listener.on_event(message.clone(), i); 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /fixtures/callbacks-regression/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | jsi 2 | wasm 3 | -------------------------------------------------------------------------------- /fixtures/callbacks-regression/tests/bindings/test_callbacks_regression.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | /* 8 | fixture=fixtures/callbacks-regression 9 | cargo xtask run \ 10 | --crate $fixture \ 11 | --ts-dir $fixture/generated \ 12 | --cpp-dir $fixture/generated/cpp \ 13 | $fixture/tests/bindings/test_callbacks_regression.ts 14 | */ 15 | 16 | import generated, { 17 | EventSource, 18 | EventListener, 19 | } from "../../generated/uniffi_callbacks"; 20 | import { asyncTest, AsyncAsserts } from "@/asserts"; 21 | import "@/polyfills"; 22 | 23 | // This is only needed in tests. 24 | generated.initialize(); 25 | 26 | class EventListenerImpl implements EventListener { 27 | constructor( 28 | private t: AsyncAsserts, 29 | private max: number, 30 | ) {} 31 | onEvent(message: string, number: number): void { 32 | // console.log("--", message, number); 33 | if (number === this.max - 1) { 34 | console.log("-- Done!", this.max); 35 | this.t.end(); 36 | } 37 | } 38 | } 39 | 40 | async function testToMax(max: number, t: AsyncAsserts) { 41 | const listener = new EventListenerImpl(t, max); 42 | const source = new EventSource(listener); 43 | source.start(`Going to ${max}, now at:`, max); 44 | } 45 | 46 | (async () => { 47 | for (let i = 1; i <= 4096; i *= 2) { 48 | await asyncTest( 49 | `Full tilt test up to ${i}`, 50 | async (t) => await testToMax(i, t), 51 | ); 52 | } 53 | })(); 54 | -------------------------------------------------------------------------------- /fixtures/callbacks-regression/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/callbacks-regression/uniffi.toml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhugman/uniffi-bindgen-react-native/2de076e95efafcfa26350eb51ace6cc22f4aff84/fixtures/callbacks-regression/uniffi.toml -------------------------------------------------------------------------------- /fixtures/callbacks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-callbacks" 3 | version = "0.22.0" 4 | authors = ["Firefox Sync Team "] 5 | edition = "2021" 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["lib", "cdylib"] 11 | name = "uniffi_fixture_callbacks" 12 | 13 | [dependencies] 14 | uniffi = { workspace = true, features=[] } 15 | thiserror = "1.0" 16 | 17 | [build-dependencies] 18 | uniffi = { workspace = true, features = ["build"] } 19 | 20 | [dev-dependencies] 21 | uniffi = { workspace = true, features = ["bindgen-tests"] } 22 | -------------------------------------------------------------------------------- /fixtures/callbacks/README.md: -------------------------------------------------------------------------------- 1 | # A "Callbacks" test for uniffi components 2 | 3 | This is similar to the `callbacks` example, but it's intended to be contrived and to 4 | ensure we get good test coverage of all possible options. 5 | 6 | It's here so it doesn't even need to make a cursory effort to be a "good" 7 | example; it intentionally panics, asserts params are certain values, has 8 | no-op methods etc. If you're trying to get your head around uniffi then the 9 | "examples" directory will be a much better bet. 10 | 11 | This is its own crate, because the callback mechanism is not implemented for all backends yet. 12 | -------------------------------------------------------------------------------- /fixtures/callbacks/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("src/callbacks.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/callbacks/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/chronological/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-time" 3 | edition = "2021" 4 | version = "0.22.0" 5 | authors = ["Firefox Sync Team "] 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["lib", "cdylib"] 11 | name = "uniffi_chronological" 12 | 13 | [dependencies] 14 | uniffi = { workspace = true } 15 | thiserror = "1.0" 16 | chrono = { version = "0.4.23", default-features = false, features = ["alloc", "std"] } 17 | 18 | [build-dependencies] 19 | uniffi = { workspace = true, features = ["build"] } 20 | 21 | [dev-dependencies] 22 | uniffi = { workspace = true, features = ["bindgen-tests"] } 23 | -------------------------------------------------------------------------------- /fixtures/chronological/README.md: -------------------------------------------------------------------------------- 1 | # Test for time types 2 | 3 | This directory contains tests for Timestamp and Duration types. It is intended 4 | to exercise these types and their edge cases. -------------------------------------------------------------------------------- /fixtures/chronological/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("src/chronological.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/chronological/src/chronological.udl: -------------------------------------------------------------------------------- 1 | [Error] 2 | enum ChronologicalError { 3 | "TimeOverflow", 4 | "TimeDiffError", 5 | }; 6 | 7 | namespace chronological { 8 | [Throws=ChronologicalError] 9 | timestamp return_timestamp(timestamp a); 10 | 11 | [Throws=ChronologicalError] 12 | duration return_duration(duration a); 13 | 14 | string to_string_timestamp(timestamp a); 15 | 16 | timestamp get_pre_epoch_timestamp(); 17 | 18 | [Throws=ChronologicalError] 19 | timestamp add(timestamp a, duration b); 20 | 21 | [Throws=ChronologicalError] 22 | duration diff(timestamp a, timestamp b); 23 | 24 | timestamp now(); 25 | 26 | boolean equal(timestamp a, timestamp b); 27 | 28 | boolean optional(timestamp? a, duration? b); 29 | 30 | [Throws=ChronologicalError] 31 | u64 get_seconds_before_unix_epoch(timestamp a); 32 | 33 | [Throws=ChronologicalError] 34 | timestamp set_seconds_before_unix_epoch(u64 seconds); 35 | }; 36 | -------------------------------------------------------------------------------- /fixtures/chronological/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | jsi 2 | wasm 3 | -------------------------------------------------------------------------------- /fixtures/chronological/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/coverall/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-coverall" 3 | version = "0.22.0" 4 | authors = ["Firefox Sync Team "] 5 | edition = "2021" 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["lib", "staticlib", "cdylib"] 11 | name = "uniffi_coverall" 12 | 13 | [dependencies] 14 | uniffi = { workspace = true, features=[]} 15 | once_cell = "1.12" 16 | thiserror = "1.0" 17 | 18 | [build-dependencies] 19 | uniffi = { workspace = true, features = ["build"] } 20 | 21 | [dev-dependencies] 22 | uniffi = { workspace = true, features = ["bindgen-tests"] } 23 | uniffi_meta = { workspace = true } 24 | -------------------------------------------------------------------------------- /fixtures/coverall/README.md: -------------------------------------------------------------------------------- 1 | # A "Coverall" test for uniffi components 2 | 3 | This is similar to our examples, but it's intended to be contrived and to 4 | ensure we get good test coverage of all possible options. 5 | 6 | It's here so it doesn't even need to make a cursory effort to be a "good" 7 | example; it intentionally panics, asserts params are certain values, has 8 | no-op methods etc. If you're trying to get your head around uniffi then the 9 | "examples" directory will be a much better bet. 10 | -------------------------------------------------------------------------------- /fixtures/coverall/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("src/coverall.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/coverall/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/coverall2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-coverall2" 3 | edition = "2021" 4 | version = "0.22.0" 5 | license = "MPL-2.0" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["lib", "cdylib"] 10 | 11 | [dependencies] 12 | async-std = "1.12.0" 13 | thiserror = "1.0" 14 | uniffi = { workspace = true } 15 | 16 | [build-dependencies] 17 | uniffi = { workspace = true, features = ["build"] } 18 | 19 | [dev-dependencies] 20 | uniffi = { workspace = true, features = ["bindgen-tests"] } 21 | -------------------------------------------------------------------------------- /fixtures/coverall2/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | #[uniffi::export] 8 | /// This makes the byte array in rust, and the test in JS will compare it there. 9 | /// 10 | /// This eliminates the possibility of two symmetrical bugs in each of the lift and 11 | /// lower for the roundtrip tests– this just uses the Rust lower, and the Typescript 12 | /// lift. 13 | pub fn well_known_array_buffer() -> Vec { 14 | Default::default() 15 | } 16 | 17 | #[uniffi::export] 18 | /// This uses a byte array to pass an argument and return, so it uses lift/lower methods. 19 | pub fn identity_array_buffer(bytes: Vec) -> Vec { 20 | bytes 21 | } 22 | 23 | #[uniffi::export] 24 | /// This uses an option to force the lift/lower machinery to use read and write 25 | /// directly from the Option lift and lower, not from the byte array lift and lower. 26 | pub fn identity_array_buffer_forced_read(bytes: Option>) -> Option> { 27 | bytes 28 | } 29 | 30 | #[uniffi::export] 31 | pub fn identity_nested_optional(value: Option>) -> Option> { 32 | value 33 | } 34 | 35 | #[uniffi::export] 36 | pub fn match_nested_optional(value: Option>) -> i8 { 37 | let Some(inner) = value else { 38 | // if value.is_none 39 | return 0; 40 | }; 41 | if inner.is_none() { 42 | 1 43 | } else { 44 | 2 45 | } 46 | } 47 | 48 | uniffi::setup_scaffolding!(); 49 | -------------------------------------------------------------------------------- /fixtures/coverall2/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/coverall2/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/custom-types-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-example-custom-types" 3 | edition = "2021" 4 | version = "0.22.0" 5 | authors = ["Firefox Sync Team "] 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["lib", "cdylib"] 11 | name = "custom_types" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | bytes = "1.3" 16 | uniffi = { workspace = true } 17 | url = "2.2" 18 | 19 | [build-dependencies] 20 | uniffi = { workspace = true, features = ["build"] } 21 | 22 | [dev-dependencies] 23 | uniffi = { workspace = true, features = ["bindgen-tests"] } 24 | -------------------------------------------------------------------------------- /fixtures/custom-types-example/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("src/custom-types.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/custom-types-example/src/custom-types.udl: -------------------------------------------------------------------------------- 1 | [Custom] 2 | typedef string Url; 3 | 4 | [Custom] 5 | typedef i64 Handle; 6 | 7 | [Custom] 8 | typedef i64 TimeIntervalMs; 9 | 10 | [Custom] 11 | typedef f64 TimeIntervalSecDbl; 12 | 13 | [Custom] 14 | typedef f32 TimeIntervalSecFlt; 15 | 16 | dictionary CustomTypesDemo { 17 | Url url; 18 | Handle handle; 19 | TimeIntervalMs time_interval_ms; 20 | TimeIntervalSecDbl time_interval_sec_dbl; 21 | TimeIntervalSecFlt time_interval_sec_flt; 22 | }; 23 | 24 | namespace custom_types { 25 | CustomTypesDemo get_custom_types_demo(CustomTypesDemo? demo); 26 | }; 27 | -------------------------------------------------------------------------------- /fixtures/custom-types-example/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/custom-types-example/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/enum-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-enum-types" 3 | version = "0.22.0" 4 | authors = ["Firefox Sync Team "] 5 | edition = "2021" 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["lib", "cdylib"] 11 | name = "enum_types" 12 | 13 | [dependencies] 14 | uniffi = { workspace = true } 15 | thiserror = "1.0" 16 | 17 | [build-dependencies] 18 | uniffi = { workspace = true, features = ["build"] } 19 | 20 | [dev-dependencies] 21 | uniffi = { workspace = true, features = ["bindgen-tests"] } 22 | -------------------------------------------------------------------------------- /fixtures/enum-types/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("src/enum_types.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/enum-types/src/enum_types.udl: -------------------------------------------------------------------------------- 1 | namespace enum_types {}; 2 | enum Animal { 3 | "Dog", 4 | "Cat" 5 | }; 6 | -------------------------------------------------------------------------------- /fixtures/enum-types/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/enum-types/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/error-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-error-types" 3 | version = "0.22.0" 4 | edition = "2021" 5 | license = "MPL-2.0" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["lib", "cdylib"] 10 | name = "uniffi_error_types" 11 | 12 | [dependencies] 13 | uniffi = { workspace = true } 14 | anyhow = "1" 15 | thiserror = "1.0" 16 | 17 | [build-dependencies] 18 | uniffi = { workspace = true, features = ["build"] } 19 | 20 | [dev-dependencies] 21 | uniffi = { workspace = true, features = ["bindgen-tests"] } 22 | -------------------------------------------------------------------------------- /fixtures/error-types/README.md: -------------------------------------------------------------------------------- 1 | Tests for objects as errors. 2 | 3 | This works well with explicit type wrangling: 4 | 5 | ```rust 6 | fn oops() -> Result<(), Arc> { 7 | let e = anyhow::Error::msg("oops"); 8 | Err(Arc::new(e.into())) 9 | } 10 | ``` 11 | 12 | But a goal is to allow: 13 | 14 | ```rust 15 | // doesn't work 16 | fn oops() -> anyhow::Result<()> { 17 | anyhow::bail!("oops"); 18 | } 19 | ``` 20 | 21 | # Stuck! 22 | 23 | the above uniffi expands to: 24 | 25 | ```rust 26 | extern "C" fn uniffi_uniffi_error_types_fn_func_oops( 27 | call_status: &mut ::uniffi::RustCallStatus, 28 | ) -> <::std::result::Result< 29 | (), 30 | std::sync::Arc, 31 | > as ::uniffi::LowerReturn>::ReturnType { 32 | ... 33 | ::uniffi::rust_call( 34 | call_status, 35 | || { 36 | <::std::result::Result<(), std::sync::Arc> as ::uniffi::LowerReturn ...>::lower_return( 37 | match uniffi_lift_args() { 38 | Ok(uniffi_args) => oops().map_err(::std::convert::Into::into), 39 | Err((arg_name, anyhow_error)) => ... 40 | }, 41 | ) 42 | }, 43 | ) 44 | } 45 | ``` 46 | 47 | # Problem is: 48 | ```rust 49 | Ok(uniffi_args) => oops().map_err(::std::convert::Into::into), 50 | ``` 51 | 52 | map_err has `anyhow::Error<>`, we want `Arc`, `::into` can't do that. 53 | 54 | This works for enum because all `Arc`s above are `ErrorEnum`. So above call is more like: 55 | map_err has `CrateInternalError`, we want `CratePublicError`, `::into` can do that. 56 | -------------------------------------------------------------------------------- /fixtures/error-types/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("./src/error_types.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/error-types/src/error_types.udl: -------------------------------------------------------------------------------- 1 | namespace error_types { 2 | [Throws=ErrorInterface] 3 | void oops(); 4 | 5 | [Throws=ErrorInterface] 6 | void oops_nowrap(); 7 | 8 | ErrorInterface get_error(string message); 9 | 10 | [Throws=RichError] 11 | void throw_rich(string message); 12 | }; 13 | 14 | interface TestInterface { 15 | constructor(); 16 | 17 | [Throws=ErrorInterface, Name="fallible_new"] 18 | constructor(); 19 | 20 | [Throws=ErrorInterface] 21 | void oops(); 22 | }; 23 | 24 | [Traits=(Debug, Display)] 25 | interface ErrorInterface { 26 | sequence chain(); 27 | string? link(u64 index); 28 | }; 29 | 30 | // Kotlin replaces a trailing "Error" with "Exception" 31 | // for enums, so we should check it does for objects too. 32 | [Traits=(Debug, Display)] 33 | interface RichError { 34 | }; 35 | -------------------------------------------------------------------------------- /fixtures/error-types/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | jsi 2 | wasm 3 | -------------------------------------------------------------------------------- /fixtures/error-types/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/ext-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-ext-types" 3 | edition = "2021" 4 | version = "0.22.0" 5 | authors = ["Firefox Sync Team "] 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [package.metadata.uniffi.testing] 10 | external-crates = [ 11 | "uniffi-fixture-ext-types-custom-types", 12 | "uniffi-fixture-ext-types-lib-one", 13 | "uniffi-fixture-ext-types-external-crate", 14 | "uniffi-fixture-ext-types-sub-lib", 15 | "uniffi-example-custom-types", 16 | ] 17 | 18 | [lib] 19 | crate-type = ["lib", "cdylib"] 20 | name = "uniffi_ext_types_lib" 21 | 22 | [dependencies] 23 | anyhow = "1" 24 | bytes = "1.3" 25 | uniffi = { workspace = true } 26 | 27 | uniffi-fixture-ext-types-external-crate = {path = "subcrates/external-crate"} 28 | uniffi-fixture-ext-types-lib-one = {path = "subcrates/uniffi-one"} 29 | uniffi-fixture-ext-types-custom-types = {path = "subcrates/custom-types"} 30 | uniffi-fixture-ext-types-sub-lib = {path = "subcrates/sub-lib"} 31 | 32 | # Reuse one of our examples. 33 | uniffi-example-custom-types = {path = "../custom-types-example"} 34 | 35 | url = "2.2" 36 | 37 | [build-dependencies] 38 | uniffi = { workspace = true, features = ["build"] } 39 | 40 | [dev-dependencies] 41 | uniffi = { workspace = true, features = ["bindgen-tests"] } 42 | -------------------------------------------------------------------------------- /fixtures/ext-types/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("src/ext-types-lib.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/custom-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-ext-types-custom-types" 3 | edition = "2021" 4 | version = "0.22.0" 5 | authors = ["Firefox Sync Team "] 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["lib", "cdylib"] 11 | name = "ext_types_custom" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | bytes = "1.3" 16 | thiserror = "1.0" 17 | uniffi = { workspace = true } 18 | 19 | [build-dependencies] 20 | uniffi = { workspace = true, features = ["build"] } 21 | 22 | [dev-dependencies] 23 | uniffi = { workspace = true, features = ["bindgen-tests"] } 24 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/custom-types/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("src/custom_types.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/custom-types/src/custom_types.udl: -------------------------------------------------------------------------------- 1 | [Custom] 2 | typedef string Guid; 3 | 4 | // Wrapping another custom type. 5 | [Custom] 6 | typedef Guid ANestedGuid; 7 | 8 | [Error] 9 | enum GuidError { 10 | "TooShort" 11 | }; 12 | 13 | dictionary GuidHelper { 14 | Guid guid; 15 | sequence guids; 16 | Guid? maybe_guid; 17 | }; 18 | 19 | callback interface GuidCallback { 20 | Guid run(Guid arg); 21 | }; 22 | 23 | namespace ext_types_custom { 24 | // Note this intentionally does not throw an error - uniffi will panic if 25 | // a Guid can't be converted. 26 | Guid get_guid(optional Guid? value); 27 | 28 | // Uniffi will handle failure converting a string to a Guid correctly if 29 | // the conversion returns `Err(GuidError)`, or panic otherwise. 30 | [Throws=GuidError] 31 | Guid try_get_guid(optional Guid? value); 32 | 33 | GuidHelper get_guid_helper(optional GuidHelper? values); 34 | Guid run_callback(GuidCallback callback); 35 | }; 36 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/custom-types/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | uniffi::build_foreign_language_testcases!("tests/bindings/test_guid.py",); 7 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/external-crate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-ext-types-external-crate" 3 | edition = "2021" 4 | version = "0.22.0" 5 | authors = ["Firefox Sync Team "] 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["lib", "cdylib"] 11 | name = "ext_types_external_crate" 12 | 13 | [dependencies] 14 | # NOTE: This crate should never depend on UniFFI - see README.md for more. 15 | 16 | [build-dependencies] 17 | 18 | [dev-dependencies] 19 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/external-crate/README.md: -------------------------------------------------------------------------------- 1 | This is a crate which does not itself depend on UniFFI - however, the 2 | types in this crate are referenced externally by crates which do. 3 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/external-crate/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | // This crate does not use UniFFI, but it exposes some types which are used by crates which do. 7 | // This type is referenced as an "external" type as a dictionary. 8 | pub struct ExternalCrateDictionary { 9 | pub sval: String, 10 | } 11 | 12 | pub struct ExternalCrateInterface { 13 | pub sval: String, 14 | } 15 | 16 | #[non_exhaustive] 17 | pub enum ExternalCrateNonExhaustiveEnum { 18 | One, 19 | Two, 20 | } 21 | 22 | // This type is referenced as an "external" type as an interface. 23 | impl ExternalCrateInterface { 24 | pub fn new(sval: String) -> Self { 25 | ExternalCrateInterface { sval } 26 | } 27 | 28 | pub fn value(&self) -> String { 29 | self.sval.clone() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/sub-lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-ext-types-sub-lib" 3 | edition = "2021" 4 | version = "0.22.0" 5 | authors = ["Firefox Sync Team "] 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [package.metadata.uniffi.testing] 10 | external-crates = [ 11 | "uniffi-fixture-ext-types-lib-one", 12 | ] 13 | 14 | [lib] 15 | crate-type = ["lib", "cdylib"] 16 | name = "uniffi_sublib" 17 | 18 | [dependencies] 19 | anyhow = "1" 20 | uniffi = { workspace = true } 21 | uniffi-fixture-ext-types-lib-one = {path = "../uniffi-one"} 22 | 23 | [build-dependencies] 24 | uniffi = { workspace = true, features = ["build"] } 25 | 26 | [dev-dependencies] 27 | uniffi = { workspace = true, features = ["bindgen-tests"] } 28 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/sub-lib/README.md: -------------------------------------------------------------------------------- 1 | This is a "sub library" - ie, a crate which consumes types from 2 | other external crates and *also* exports its own composite types and trait 3 | implementations to be consumed by the "main" library. 4 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/sub-lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use std::sync::Arc; 7 | use uniffi_one::{UniffiOneEnum, UniffiOneInterface, UniffiOneTrait}; 8 | 9 | #[derive(Default, uniffi::Record)] 10 | pub struct SubLibType { 11 | pub maybe_enum: Option, 12 | pub maybe_trait: Option>, 13 | pub maybe_interface: Option>, 14 | } 15 | 16 | #[uniffi::export] 17 | fn get_sub_type(existing: Option) -> SubLibType { 18 | existing.unwrap_or_default() 19 | } 20 | 21 | struct OneImpl; 22 | 23 | impl UniffiOneTrait for OneImpl { 24 | fn hello(&self) -> String { 25 | "sub-lib trait impl says hello".to_string() 26 | } 27 | } 28 | 29 | #[uniffi::export] 30 | fn get_trait_impl() -> Arc { 31 | Arc::new(OneImpl {}) 32 | } 33 | 34 | uniffi::setup_scaffolding!("imported_types_sublib"); 35 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/sub-lib/uniffi.toml: -------------------------------------------------------------------------------- 1 | [bindings.typescript] 2 | logLevel = "debug" 3 | consoleImport = "@/hermes" 4 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/uniffi-one/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-ext-types-lib-one" 3 | edition = "2021" 4 | version = "0.22.0" 5 | authors = ["Firefox Sync Team "] 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["lib", "cdylib"] 11 | # Note the crate name is different from the namespace used by this crate. 12 | name = "uniffi_one" 13 | 14 | [dependencies] 15 | anyhow = "1" 16 | bytes = "1.3" 17 | uniffi = { workspace = true } 18 | 19 | [build-dependencies] 20 | uniffi = { workspace = true, features = ["build"] } 21 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/uniffi-one/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("src/uniffi-one.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/uniffi-one/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use std::sync::atomic::{AtomicI32, Ordering}; 7 | 8 | pub struct UniffiOneType { 9 | pub sval: String, 10 | } 11 | 12 | pub enum UniffiOneEnum { 13 | One, 14 | Two, 15 | } 16 | 17 | #[derive(uniffi::Record)] 18 | pub struct UniffiOneProcMacroType { 19 | pub sval: String, 20 | } 21 | 22 | #[derive(Default)] 23 | pub struct UniffiOneInterface { 24 | current: AtomicI32, 25 | } 26 | 27 | impl UniffiOneInterface { 28 | pub fn new() -> Self { 29 | Self::default() 30 | } 31 | 32 | pub fn increment(&self) -> i32 { 33 | self.current.fetch_add(1, Ordering::Relaxed) + 1 34 | } 35 | } 36 | 37 | #[uniffi::export] 38 | fn get_my_proc_macro_type(t: UniffiOneProcMacroType) -> UniffiOneProcMacroType { 39 | t 40 | } 41 | 42 | #[uniffi::export] 43 | async fn get_uniffi_one_async() -> UniffiOneEnum { 44 | UniffiOneEnum::One 45 | } 46 | 47 | #[uniffi::export(with_foreign)] 48 | pub trait UniffiOneTrait: Send + Sync { 49 | fn hello(&self) -> String; 50 | } 51 | 52 | // Note `UDL` vs `Udl` is important here to test foreign binding name fixups. 53 | pub trait UniffiOneUDLTrait: Send + Sync { 54 | fn hello(&self) -> String; 55 | } 56 | 57 | uniffi::include_scaffolding!("uniffi-one"); 58 | -------------------------------------------------------------------------------- /fixtures/ext-types/subcrates/uniffi-one/src/uniffi-one.udl: -------------------------------------------------------------------------------- 1 | namespace uniffi_one_ns {}; 2 | 3 | dictionary UniffiOneType { 4 | string sval; 5 | }; 6 | 7 | enum UniffiOneEnum { 8 | "One", 9 | "Two", 10 | }; 11 | 12 | interface UniffiOneInterface { 13 | constructor(); 14 | 15 | i32 increment(); 16 | }; 17 | 18 | [Trait, WithForeign] 19 | interface UniffiOneUDLTrait { 20 | string hello(); 21 | }; 22 | -------------------------------------------------------------------------------- /fixtures/ext-types/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/ext-types/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/ext-types/uniffi.toml: -------------------------------------------------------------------------------- 1 | [bindings.typescript] 2 | logLevel = "debug" 3 | consoleImport = "@/hermes" 4 | 5 | [bindings.python.external_packages] 6 | # This fixture does not create a Python package, so we want all modules to be top-level modules. 7 | custom_types = "" 8 | ext_types_custom = "" 9 | uniffi_one_ns = "" 10 | imported_types_sublib = "" 11 | -------------------------------------------------------------------------------- /fixtures/futures-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-example-futures" 3 | edition = "2021" 4 | version = "0.22.0" 5 | authors = ["Firefox Sync Team "] 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["lib", "cdylib"] 11 | 12 | [dependencies] 13 | async-std = "1.12.0" 14 | thiserror = "1.0" 15 | uniffi = { workspace = true } 16 | 17 | [build-dependencies] 18 | uniffi = { workspace = true, features = ["build"] } 19 | 20 | [dev-dependencies] 21 | uniffi = { workspace = true, features = ["bindgen-tests"] } 22 | -------------------------------------------------------------------------------- /fixtures/futures-example/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use async_std::future::{pending, timeout}; 8 | use std::time::Duration; 9 | 10 | /// Async function that says something after a certain time. 11 | #[uniffi::export] 12 | pub async fn say_after(ms: u64, who: String) -> String { 13 | let never = pending::<()>(); 14 | timeout(Duration::from_millis(ms), never).await.unwrap_err(); 15 | format!("Hello, {who}!") 16 | } 17 | 18 | uniffi::setup_scaffolding!(); 19 | -------------------------------------------------------------------------------- /fixtures/futures-example/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/futures-example/tests/bindings/test_futures_example.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | import { sayAfter } from "../../generated/uniffi_example_futures"; 7 | import { asyncTest } from "@/asserts"; 8 | import "@/polyfills"; 9 | 10 | asyncTest("sayAfter once", async (t): Promise => { 11 | const result = await sayAfter(BigInt("500"), "World"); 12 | t.assertEqual(result, `Hello, World!`); 13 | t.end(); 14 | }); 15 | 16 | asyncTest("sayAfter multiple times", async (t): Promise => { 17 | const strings = ["World", "Async", "Test", "Rust"]; 18 | for (const s of strings) { 19 | const result = await sayAfter(BigInt("100"), s); 20 | t.assertEqual(result, `Hello, ${s}!`); 21 | } 22 | t.end(); 23 | }); 24 | -------------------------------------------------------------------------------- /fixtures/futures-example/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/futures/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-futures" 3 | version = "0.21.0" 4 | authors = ["Ivan Enderlin "] 5 | edition = "2021" 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | name = "uniffi_futures" 11 | crate-type = ["lib", "cdylib"] 12 | 13 | [[bin]] 14 | name = "uniffi-fixtures-futures" 15 | path = "src/bin.rs" 16 | 17 | [dependencies] 18 | uniffi = { workspace = true, features = ["tokio", "cli"] } 19 | async-trait = "0.1" 20 | futures = "0.3" 21 | thiserror = "1.0" 22 | tokio = { version = "1.24.1", features = ["time", "sync"] } 23 | once_cell = "1.18.0" 24 | 25 | [build-dependencies] 26 | uniffi = { workspace = true, features = ["build"] } 27 | 28 | [dev-dependencies] 29 | uniffi = { workspace = true, features = ["bindgen-tests"] } 30 | -------------------------------------------------------------------------------- /fixtures/futures/README.md: -------------------------------------------------------------------------------- 1 | # A basic test for uniffi components 2 | 3 | This test covers async functions and methods. It also provides examples. 4 | 5 | ## Run the tests 6 | 7 | Simply use `cargo`: 8 | 9 | ```sh 10 | $ cargo test 11 | ``` 12 | 13 | It is possible to filter by test names, like `cargo test -- swift` to only run 14 | Swift's tests. 15 | 16 | ## Run the examples 17 | 18 | At the time of writing, each `examples/*` directory has a `Makefile`. They are 19 | mostly designed for Unix-ish systems, sorry for that. 20 | 21 | To run the examples, first `uniffi` must be compiled: 22 | 23 | ```sh 24 | $ cargo build --release -p uniffi` 25 | ``` 26 | 27 | Then, each `Makefile` has 2 targets: `build` and `run`: 28 | 29 | ```sh 30 | $ # Build the examples. 31 | $ make build 32 | $ 33 | $ # Run the example. 34 | $ make run 35 | ``` 36 | 37 | One note for `examples/kotlin/`, some JAR files must be present, so please 38 | run `make install-jar` first: It will just download the appropriated JAR files 39 | directly inside the directory from Maven. -------------------------------------------------------------------------------- /fixtures/futures/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | fn main() { 7 | uniffi::generate_scaffolding("src/futures.udl").unwrap(); 8 | } 9 | -------------------------------------------------------------------------------- /fixtures/futures/src/bin.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | fn main() { 7 | uniffi::uniffi_bindgen_main() 8 | } 9 | -------------------------------------------------------------------------------- /fixtures/futures/src/futures.udl: -------------------------------------------------------------------------------- 1 | namespace futures { 2 | [Async] 3 | boolean always_ready(); 4 | }; 5 | 6 | [Trait] 7 | interface SayAfterUdlTrait { 8 | [Async] 9 | string say_after(u16 ms, string who); 10 | }; 11 | 12 | interface UdlMegaphone { 13 | [Async] 14 | constructor(); 15 | 16 | [Async, Name="secondary"] 17 | constructor(); 18 | 19 | [Async] 20 | string say_after(u16 ms, string who); 21 | }; 22 | -------------------------------------------------------------------------------- /fixtures/futures/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | jsi 2 | -------------------------------------------------------------------------------- /fixtures/futures/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/futures/uniffi.toml: -------------------------------------------------------------------------------- 1 | [bindings.typescript] 2 | logLevel = "debug" 3 | consoleImport = "@/hermes" 4 | -------------------------------------------------------------------------------- /fixtures/gc-callbacks-crasher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gc-callbacks-crasher" 3 | edition = "2021" 4 | version = "0.22.0" 5 | license = "MPL-2.0" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["lib", "cdylib"] 10 | 11 | [dependencies] 12 | async-std = "1.12.0" 13 | async-trait = "0.1.83" 14 | thiserror = "1.0" 15 | uniffi = { workspace = true, features = ["tokio"] } 16 | 17 | [build-dependencies] 18 | uniffi = { workspace = true, features = ["build"] } 19 | 20 | [dev-dependencies] 21 | uniffi = { workspace = true, features = ["bindgen-tests"] } 22 | -------------------------------------------------------------------------------- /fixtures/gc-callbacks-crasher/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | jsi 2 | wasm 3 | -------------------------------------------------------------------------------- /fixtures/rondpoint-procmacro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-example-rondpoint-procmacro" 3 | edition = "2021" 4 | version = "0.22.0" 5 | license = "MPL-2.0" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["lib", "staticlib", "cdylib"] 10 | name = "uniffi_rondpointpm" 11 | 12 | [dependencies] 13 | uniffi = { workspace = true } 14 | 15 | [build-dependencies] 16 | uniffi = { workspace = true, features = ["build"] } 17 | 18 | [dev-dependencies] 19 | uniffi = { workspace = true, features = ["bindgen-tests"] } 20 | -------------------------------------------------------------------------------- /fixtures/rondpoint-procmacro/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/rondpoint-procmacro/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/rondpoint/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-example-rondpoint" 3 | edition = "2021" 4 | version = "0.22.0" 5 | authors = ["Firefox Sync Team "] 6 | license = "MPL-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["lib", "staticlib", "cdylib"] 11 | name = "uniffi_rondpoint" 12 | 13 | [dependencies] 14 | uniffi = { workspace = true } 15 | 16 | [build-dependencies] 17 | uniffi = { workspace = true, features = ["build"] } 18 | 19 | [dev-dependencies] 20 | uniffi = { workspace = true, features = ["bindgen-tests"] } 21 | -------------------------------------------------------------------------------- /fixtures/rondpoint/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("src/rondpoint.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/rondpoint/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/rondpoint/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /fixtures/trait-methods/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-fixture-trait-methods" 3 | version = "0.22.0" 4 | edition = "2021" 5 | license = "MPL-2.0" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["lib", "cdylib"] 10 | name = "uniffi_trait_methods" 11 | 12 | [dependencies] 13 | uniffi = { workspace = true } 14 | once_cell = "1.12" 15 | thiserror = "1.0" 16 | 17 | [build-dependencies] 18 | uniffi = { workspace = true, features = ["build"] } 19 | 20 | [dev-dependencies] 21 | uniffi = { workspace = true, features = ["bindgen-tests"] } 22 | 23 | -------------------------------------------------------------------------------- /fixtures/trait-methods/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | fn main() { 8 | uniffi::generate_scaffolding("./src/trait_methods.udl").unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/trait-methods/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use std::sync::Arc; 8 | 9 | #[derive(Debug, PartialEq, Eq, Hash)] 10 | pub struct TraitMethods { 11 | val: String, 12 | } 13 | 14 | impl TraitMethods { 15 | fn new(val: String) -> Self { 16 | Self { val } 17 | } 18 | } 19 | 20 | impl std::fmt::Display for TraitMethods { 21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 22 | write!(f, "TraitMethods({})", self.val) 23 | } 24 | } 25 | 26 | #[derive(Debug, PartialEq, Eq, Hash, uniffi::Object)] 27 | #[uniffi::export(Debug, Display, Eq, Hash)] 28 | pub struct ProcTraitMethods { 29 | val: String, 30 | } 31 | 32 | #[uniffi::export] 33 | impl ProcTraitMethods { 34 | #[uniffi::constructor] 35 | fn new(val: String) -> Arc { 36 | Arc::new(Self { val }) 37 | } 38 | } 39 | 40 | impl std::fmt::Display for ProcTraitMethods { 41 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 42 | write!(f, "ProcTraitMethods({})", self.val) 43 | } 44 | } 45 | 46 | uniffi::include_scaffolding!("trait_methods"); 47 | -------------------------------------------------------------------------------- /fixtures/trait-methods/src/trait_methods.udl: -------------------------------------------------------------------------------- 1 | namespace trait_methods {}; 2 | 3 | [Traits=(Display, Debug, Eq, Hash)] 4 | interface TraitMethods { 5 | constructor(string name); 6 | }; 7 | -------------------------------------------------------------------------------- /fixtures/trait-methods/tests/bindings/.supported-flavors.txt: -------------------------------------------------------------------------------- 1 | wasm 2 | jsi 3 | -------------------------------------------------------------------------------- /fixtures/trait-methods/tests/bindings/test_trait_methods.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | import { TraitMethods } from "../../generated/trait_methods"; 7 | import { test } from "@/asserts"; 8 | 9 | test("toString() is generated", (t) => { 10 | const m = new TraitMethods("yo"); 11 | t.assertEqual(m.toString(), "TraitMethods(yo)"); 12 | t.assertEqual("" + m, "TraitMethods(yo)"); 13 | t.assertEqual(`${m}`, "TraitMethods(yo)"); 14 | }); 15 | 16 | test("equals is generated", (t) => { 17 | const m = new TraitMethods("yo"); 18 | t.assertTrue(m.equals(new TraitMethods("yo"))); 19 | }); 20 | 21 | test("hashCode is generated", (t) => { 22 | const m = new TraitMethods("yo"); 23 | const map = new Map([ 24 | [m.hashCode(), 1], 25 | [new TraitMethods("yoyo").hashCode(), 2], 26 | ]); 27 | t.assertEqual(map.get(m.hashCode()), 1); 28 | t.assertEqual(map.get(new TraitMethods("yoyo").hashCode()), 2); 29 | }); 30 | 31 | test("toDebugString() is generated", (t) => { 32 | const m = new TraitMethods("yo"); 33 | t.assertEqual(m.toDebugString(), 'TraitMethods { val: "yo" }'); 34 | }); 35 | -------------------------------------------------------------------------------- /fixtures/trait-methods/tests/test_generated_bindings.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | -------------------------------------------------------------------------------- /integration/fixtures/compat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "codegenConfig": { 3 | "outputDir": { 4 | "ios": "ios/tmp", 5 | "android": "android/tmp" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /integration/fixtures/compat/react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dependency: { 3 | platforms: { 4 | android: { 5 | cmakeListsPath: "tmp/jni/CMakeLists.txt", 6 | }, 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /integration/fixtures/compat/ubrn.config.yaml: -------------------------------------------------------------------------------- 1 | rust: 2 | repo: https://github.com/jhugman/uniffi-starter.git 3 | branch: jhugman/bump-uniffi-to-0.29 4 | manifestPath: rust/foobar/Cargo.toml 5 | -------------------------------------------------------------------------------- /integration/fixtures/turbo-module-testing/App.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, View, Text } from "react-native"; 2 | import { 3 | Calculator, 4 | type BinaryOperator, 5 | SafeAddition, 6 | ComputationResult, 7 | } from "../../src"; 8 | 9 | // A Rust object 10 | const calculator = new Calculator(); 11 | // A Rust object implementing the Rust trait BinaryOperator 12 | const addOp = new SafeAddition(); 13 | 14 | // A Typescript class, implementing BinaryOperator 15 | class SafeMultiply implements BinaryOperator { 16 | perform(lhs: bigint, rhs: bigint): bigint { 17 | return lhs * rhs; 18 | } 19 | } 20 | const multOp = new SafeMultiply(); 21 | 22 | // bigints 23 | const three = 3n; 24 | const seven = 7n; 25 | 26 | // Perform the calculation, and to get an object 27 | // representing the computation result. 28 | const computation: ComputationResult = calculator 29 | .calculate(addOp, three, three) 30 | .calculateMore(multOp, seven) 31 | .lastResult()!; 32 | 33 | // Unpack the bigint value into a string. 34 | const result = computation.value.toString(); 35 | 36 | export default function App() { 37 | return ( 38 | 39 | Result: {result} 40 | 41 | ); 42 | } 43 | 44 | const styles = StyleSheet.create({ 45 | container: { 46 | flex: 1, 47 | alignItems: "center", 48 | justifyContent: "center", 49 | }, 50 | box: { 51 | width: 60, 52 | height: 60, 53 | marginVertical: 20, 54 | }, 55 | }); 56 | -------------------------------------------------------------------------------- /integration/fixtures/turbo-module-testing/ubrn.config.yaml: -------------------------------------------------------------------------------- 1 | rust: 2 | repo: https://github.com/jhugman/uniffi-starter.git 3 | branch: jhugman/bump-uniffi-to-0.29 4 | manifestPath: rust/foobar/Cargo.toml 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uniffi-bindgen-react-native", 3 | "version": "0.29.0-0", 4 | "description": "Uniffi bindings generator for calling Rust from React Native", 5 | "homepage": "https://github.com/jhugman/uniffi-bindgen-react-native", 6 | "repository": { 7 | "url": "https://github.com/jhugman/uniffi-bindgen-react-native.git" 8 | }, 9 | "license": "MPL-2.0", 10 | "author": { 11 | "name": "James Hugman", 12 | "email": "james@hugman.tv" 13 | }, 14 | "type": "module", 15 | "main": "./typescript/src/index.ts", 16 | "bin": { 17 | "ubrn": "./bin/cli.cjs", 18 | "uniffi-bindgen-react-native": "./bin/cli.cjs" 19 | }, 20 | "scripts": { 21 | "test": "./scripts/run-tests.sh" 22 | }, 23 | "devDependencies": { 24 | "abortcontroller-polyfill": "^1.7.5", 25 | "metro": "^0.80.8", 26 | "metro-core": "^0.80.8", 27 | "prettier": "^3.2.5", 28 | "source-licenser": "^2.0.6", 29 | "tsc-alias": "^1.8.8", 30 | "tsx": "^4.19.2", 31 | "typescript": "^5.4.5" 32 | }, 33 | "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" 34 | } 35 | -------------------------------------------------------------------------------- /scripts/run-bootstrap-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cargo install mdbook 4 | cargo install mdbook-admonish 5 | cargo install mdbook-linkcheck 6 | cargo install mdbook-open-on-gh 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 4 | "module": "commonjs", /* Specify what module code is generated. */ 5 | "paths": { 6 | "uniffi-bindgen-react-native": ["./typescript/src/index"],/* Specify a set of entries that re-map imports to additional lookup locations. */ 7 | "@/*": ["./typescript/testing/*"], 8 | }, 9 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 10 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 11 | "strict": true, /* Enable all strict type-checking options. */ 12 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 13 | }, 14 | "include": ["./typescript/src/*.ts"], 15 | "exclude": ["node_modules"] 16 | } 17 | -------------------------------------------------------------------------------- /typescript/src/enums.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | export class UniffiEnum { 7 | protected constructor(...args: any) {} 8 | } 9 | -------------------------------------------------------------------------------- /typescript/src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | // Entry point for the runtime for uniffi-bindgen-react-native. 7 | // This modules is not needed directly, but is imported from generated code. 8 | // 9 | export * from "./async-callbacks"; 10 | export * from "./async-rust-call"; 11 | export * from "./callbacks"; 12 | export * from "./enums"; 13 | export * from "./errors"; 14 | export * from "./ffi-converters"; 15 | export * from "./ffi-types"; 16 | export * from "./handle-map"; 17 | export * from "./objects"; 18 | export * from "./records"; 19 | export * from "./result"; 20 | export * from "./rust-call"; 21 | export * from "./symbols"; 22 | export * from "./type-utils"; 23 | -------------------------------------------------------------------------------- /typescript/src/records.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | /** 7 | * @param defaults function that returns the defaults of the record. This is done as a function rather than a literal so 8 | * that the defaults are calculated lazily, i.e. after everything has been declared. 9 | * @returns a function to create a new {T} with a partial that requires at least the missing keys to be present. 10 | */ 11 | export const uniffiCreateRecord = >( 12 | defaults: () => D, 13 | ) => { 14 | type MissingKeys = Omit; 15 | return (partial: Partial & Required): T => 16 | Object.freeze({ ...defaults(), ...partial } as T); 17 | }; 18 | -------------------------------------------------------------------------------- /typescript/src/result.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | import { type UniffiByteArray } from "./ffi-types"; 8 | import { type UniffiReferenceHolder } from "./callbacks"; 9 | import { type UniffiRustCallStatus } from "./rust-call"; 10 | 11 | // This Result combines RustCallStatus and ReferenceHolder. 12 | // This is principally so we can _return_ something from calling from native into typescript. 13 | 14 | export type UniffiResult = UniffiReferenceHolder | UniffiRustCallStatus; 15 | 16 | export const UniffiResult = { 17 | ready(): UniffiResult { 18 | return { code: 0 }; 19 | }, 20 | writeError( 21 | result: UniffiResult, 22 | code: number, 23 | buf: UniffiByteArray, 24 | ): UniffiResult { 25 | const status = result as UniffiRustCallStatus; 26 | status.code = code; 27 | status.errorBuf = buf; 28 | return status; 29 | }, 30 | writeSuccess(result: UniffiResult, obj: T): UniffiResult { 31 | const refHolder = result as UniffiReferenceHolder; 32 | refHolder.pointee = obj; 33 | return refHolder; 34 | }, 35 | success(pointee: T): UniffiResult { 36 | return { pointee }; 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /typescript/src/symbols.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | // Symbols for semi-private properties. These properties should 8 | // not be visible to users. 9 | // 10 | // The documentation refers to the property itself, of 11 | // which these symbols are the property name. 12 | 13 | /** 14 | * A destructor guard object is created for every 15 | * `interface` object. 16 | * 17 | * It corresponds to the `DestructibleObject` in C++, which 18 | * uses a C++ destructor to simulate the JS garbage collector. 19 | * 20 | * The implementation is in {@link RustArcPtr.h}. 21 | */ 22 | export const destructorGuardSymbol = Symbol.for("destructor"); 23 | 24 | /** 25 | * The `bigint` pointer corresponding to the Rust memory address 26 | * of the native peer. 27 | */ 28 | export const pointerLiteralSymbol = Symbol.for("pointer"); 29 | 30 | /** 31 | * The `string` name of the object, enum or error class. 32 | * 33 | * This drives the `instanceOf` method implementations. 34 | */ 35 | export const uniffiTypeNameSymbol = Symbol.for("typeName"); 36 | 37 | /** 38 | * The ordinal of the variant in an enum. 39 | * 40 | * This is the number that is passed over the FFI. 41 | */ 42 | export const variantOrdinalSymbol = Symbol.for("variant"); 43 | -------------------------------------------------------------------------------- /typescript/src/type-utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | // Utility type to ensure two types are structurally the same. 7 | export type StructuralEquality = [T] extends [U] 8 | ? [U] extends [T] 9 | ? true 10 | : false 11 | : false; 12 | -------------------------------------------------------------------------------- /typescript/testing/converters.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | export { URL } from "./hermes"; 8 | 9 | /** 10 | * This is used in both the generated code and the test. 11 | * To get it into the generated typescript, it should be part of a 12 | * custom_type in the {@link ../uniffi.toml | uniffi.toml file}. 13 | */ 14 | 15 | export function dateToSeconds(date: Date): number { 16 | return date.getTime() / 1000.0; 17 | } 18 | 19 | export function secondsToDate(seconds: number): Date { 20 | return new Date(seconds * 1000); 21 | } 22 | -------------------------------------------------------------------------------- /typescript/testing/hermes.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | declare function print(...args: any): void; 7 | 8 | export class Console { 9 | log(...args: any): void { 10 | print(...args); 11 | } 12 | 13 | info(...args: any): void { 14 | print("--", ...args); 15 | } 16 | 17 | error(...args: any): void { 18 | print("❌", ...args); 19 | } 20 | 21 | warn(...args: any): void { 22 | print("⚠️", ...args); 23 | } 24 | 25 | debug(...args: any): void { 26 | print("🤓", ...args.map(stringify)); 27 | } 28 | } 29 | 30 | export function stringify(a: any): string { 31 | return JSON.stringify(a, replacer); 32 | } 33 | 34 | function replacer(key: string, value: any): any { 35 | if (value === undefined || value === null) { 36 | return value; 37 | } 38 | if (value instanceof Set) { 39 | return [...value]; 40 | } 41 | if (value instanceof Map) { 42 | return Object.fromEntries(value); 43 | } 44 | if (typeof value === "bigint") { 45 | return `BigInt("${value}")`; 46 | } 47 | if (value.constructor !== Object && typeof value.toString === "function") { 48 | return value.toString(); 49 | } 50 | if (typeof value.asJSON === "function") { 51 | return replacer(key, value.asJSON()); 52 | } 53 | 54 | return value; 55 | } 56 | 57 | export class URL { 58 | constructor(private urlString: string) {} 59 | toString(): string { 60 | return this.urlString; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /typescript/testing/polyfills.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | import "abortcontroller-polyfill/dist/abortcontroller-polyfill-only"; 7 | import { Console as HermesConsole, URL as HermesURL } from "./hermes"; 8 | 9 | export type RuntimeContext = "nodejs" | "hermes" | "browser"; 10 | 11 | export function __runtimeContext(): RuntimeContext { 12 | if (globalThis.print !== undefined) { 13 | return "hermes"; 14 | } 15 | if (globalThis.document !== undefined) { 16 | return "browser"; 17 | } 18 | return "nodejs"; 19 | } 20 | 21 | if (globalThis.console === undefined) { 22 | (globalThis as any).console = new HermesConsole(); 23 | } 24 | if (globalThis.URL === undefined) { 25 | (globalThis as any).URL = HermesURL; 26 | } 27 | 28 | export default globalThis; 29 | -------------------------------------------------------------------------------- /typescript/testing/simulated.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | /** 7 | * This function simulates the Rust toString method, which expands the exponent, 8 | * so 1.7e6 is presented as 1700000, and 3.14e-15 is presented as 0.00000000000000314. 9 | * @param n 10 | * @returns 11 | */ 12 | export function numberToString(n: number): string { 13 | const [base, exponent] = n.toExponential().split("e"); 14 | const [whole, fractional = ""] = base.split("."); 15 | const e = parseInt(exponent, 10); 16 | const result: string[] = []; 17 | if (e > 0) { 18 | let padding = e - fractional.length; 19 | if (padding > 0) { 20 | result.push(whole, fractional, "0".repeat(padding)); 21 | } else if (padding < 0) { 22 | result.push( 23 | whole, 24 | fractional.substring(0, e), 25 | ".", 26 | fractional.substring(e), 27 | ); 28 | } else { 29 | result.push(whole, fractional); 30 | } 31 | } else if (e < 0) { 32 | result.push("0.", "0".repeat(-e - 1), whole, fractional); 33 | } else if (fractional.length > 0) { 34 | result.push(whole, ".", fractional); 35 | } else { 36 | result.push(whole); 37 | } 38 | return result.join(""); 39 | } 40 | -------------------------------------------------------------------------------- /typescript/tests/enums.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | import { test } from "../testing/asserts"; 8 | import { MyEnum } from "./playground/enums"; 9 | 10 | test("Enums have private fields", (t) => { 11 | const v1: MyEnum = new MyEnum.Variant1({ myValue: "string" }); 12 | const v2: MyEnum = new MyEnum.Variant2(42, "string"); 13 | 14 | function switchGetTag(obj: any): string { 15 | if (!MyEnum.instanceOf(obj)) { 16 | t.fail("Obj should be a MyEnum"); 17 | return ""; 18 | } 19 | const v = obj; 20 | switch (v.tag) { 21 | case "Variant1": { 22 | const { myValue } = v.inner; 23 | t.assertEqual(myValue, "string"); 24 | t.assertTrue(MyEnum.Variant1.instanceOf(v)); 25 | return v.tag; 26 | } 27 | case "Variant2": { 28 | const [p1, p2] = v.inner; 29 | t.assertEqual(p1, 42); 30 | t.assertEqual(p2, "string"); 31 | t.assertTrue(MyEnum.Variant2.instanceOf(v)); 32 | return v.tag; 33 | } 34 | } 35 | } 36 | switchGetTag(v1); 37 | switchGetTag(v2); 38 | }); 39 | -------------------------------------------------------------------------------- /typescript/tests/event-loop.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | import { asyncTest } from "../testing/asserts"; 7 | 8 | asyncTest("Dummy test that should end properly", async (t) => { 9 | t.end(); 10 | }); 11 | 12 | // asyncTest("Top level empty test that should error out", async (t) => {}); 13 | 14 | asyncTest("assertThrowsAsync catches errors", async (t) => { 15 | await t.assertThrowsAsync( 16 | () => true, 17 | async () => { 18 | throw new Error(); 19 | }, 20 | ); 21 | t.end(); 22 | }); 23 | 24 | asyncTest("t.end() ends asynchronously", async (t) => { 25 | setTimeout(async () => { 26 | t.end(); 27 | }, 20); 28 | }); 29 | -------------------------------------------------------------------------------- /typescript/tests/importing-qualified.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | import * as exported from "./playground/exported"; 8 | 9 | import { test } from "../testing/asserts"; 10 | 11 | test("Records imported as type", (t) => { 12 | const record: exported.MyRecord = { 13 | prop1: "string", 14 | prop2: 42, 15 | }; 16 | }); 17 | 18 | test("Enums imported as objects", (t) => { 19 | const enum_: exported.MyEnum = new exported.MyEnum.Variant1(); 20 | }); 21 | 22 | test("Objects interfaces imported as types", (t) => { 23 | // In our generated code, `MyObject` would not be imported into this 24 | // file because all creation happens via the `FfiConverterTypeMyObject`. 25 | const obj: exported.MyObjectInterface = new exported.MyObject(); 26 | }); 27 | 28 | test("Callback interfaces imported as types", (t) => { 29 | class Impl implements exported.MyCallbackInterface { 30 | myMethod(): void {} 31 | } 32 | 33 | const cb = new Impl(); 34 | }); 35 | 36 | test("Custom types imported as types", (t) => { 37 | const s: exported.MyCustomString = "string"; 38 | }); 39 | -------------------------------------------------------------------------------- /typescript/tests/importing.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | import { 8 | type MyCallbackInterface, 9 | type MyCustomString, 10 | MyEnum, 11 | MyObject, 12 | type MyObjectInterface, 13 | type MyRecord, 14 | } from "./playground/exported"; 15 | 16 | import { test } from "../testing/asserts"; 17 | 18 | test("Records imported as type", (t) => { 19 | const record: MyRecord = { 20 | prop1: "string", 21 | prop2: 42, 22 | }; 23 | }); 24 | 25 | test("Enums imported as objects", (t) => { 26 | const enum_: MyEnum = new MyEnum.Variant1(); 27 | }); 28 | 29 | test("Objects interfaces imported as types", (t) => { 30 | // In our generated code, `MyObject` would not be imported into this 31 | // file because all creation happens via the `FfiConverterTypeMyObject`. 32 | const obj: MyObjectInterface = new MyObject(); 33 | }); 34 | 35 | test("Callback interfaces imported as types", (t) => { 36 | class Impl implements MyCallbackInterface { 37 | myMethod(): void {} 38 | } 39 | 40 | const cb = new Impl(); 41 | }); 42 | 43 | test("Custom types imported as types", (t) => { 44 | const s: MyCustomString = "string"; 45 | }); 46 | -------------------------------------------------------------------------------- /typescript/tests/playground/exported.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | export type MyCustomString = string; 8 | 9 | export const MyEnum = (() => { 10 | return { 11 | Variant1: class {}, 12 | Variant2: class {}, 13 | instanceOf: (obj: any): boolean => true, 14 | }; 15 | })(); 16 | export type MyEnum = InstanceType< 17 | (typeof MyEnum)[keyof Omit] 18 | >; 19 | 20 | export type MyRecord = { 21 | prop1: string; 22 | prop2: number; 23 | }; 24 | 25 | export interface MyCallbackInterface { 26 | myMethod(): void; 27 | } 28 | 29 | export interface MyObjectInterface { 30 | myForeignMethod(): void; 31 | } 32 | export class MyObject implements MyObjectInterface { 33 | myForeignMethod(): void {} 34 | } 35 | -------------------------------------------------------------------------------- /typescript/tests/playground/records.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | import { uniffiCreateRecord } from "../../src/records"; 8 | 9 | export type MyRecord = { 10 | string: string; 11 | number: number; 12 | optionalString: string | undefined; 13 | bool: boolean; 14 | optionalBool: boolean | undefined; 15 | }; 16 | 17 | export const MyRecord = (() => { 18 | const defaults = () => ({ 19 | string: "default", 20 | optionalString: undefined, 21 | }); 22 | 23 | const create = uniffiCreateRecord>( 24 | defaults, 25 | ); 26 | 27 | return { 28 | create, 29 | }; 30 | })(); 31 | -------------------------------------------------------------------------------- /typescript/tests/records.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | import { Asserts, test, xtest } from "../testing/asserts"; 8 | import "../testing/polyfills"; 9 | import { MyRecord } from "./playground/records"; 10 | 11 | test("Allow defaults to be missing", (t) => { 12 | const v: MyRecord = MyRecord.create({ 13 | number: 42, 14 | bool: true, 15 | optionalBool: undefined, 16 | }); 17 | t.assertEqual(v.string, "default"); 18 | t.assertEqual(v.optionalString, undefined); 19 | t.assertEqual(v.number, 42); 20 | t.assertEqual(v.bool, true); 21 | t.assertEqual(v.optionalBool, undefined); 22 | }); 23 | 24 | test("Allow defaults to be overridden", (t) => { 25 | const v: MyRecord = MyRecord.create({ 26 | string: "overridden", 27 | optionalString: "also overridden", 28 | number: 43, 29 | bool: false, 30 | optionalBool: true, 31 | }); 32 | 33 | t.assertEqual(v.string, "overridden"); 34 | t.assertEqual(v.optionalString, "also overridden"); 35 | t.assertEqual(v.number, 43); 36 | t.assertEqual(v.bool, false); 37 | t.assertEqual(v.optionalBool, true); 38 | }); 39 | -------------------------------------------------------------------------------- /uniffi-bindgen-react-native.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 3 | 4 | Pod::Spec.new do |s| 5 | s.name = package['name'] 6 | s.version = package['version'] 7 | s.summary = package['description'] 8 | s.homepage = package['homepage'] 9 | s.license = { :type => package['license'], :file => 'LICENSE' } 10 | s.author = { package['author']['name'] => package['author']['email'] } 11 | s.source = { :git => package['repository']['url'], :tag => s.version.to_s } 12 | s.platform = :ios, '13.0' 13 | s.source_files = 'cpp/includes/*.{h,cpp,hpp}' 14 | s.swift_versions = '4.0' 15 | s.pod_target_xcconfig = { 16 | 'SWIFT_OPTIMIZATION_LEVEL' => '-Onone', 17 | } 18 | s.dependency 'React-Core' 19 | end 20 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "xtask" 9 | test = false 10 | 11 | [dependencies] 12 | anyhow = { workspace = true } 13 | camino = { workspace = true } 14 | clap = { workspace = true } 15 | pathdiff = { workspace = true } 16 | ubrn_bindgen = { path = "../crates/ubrn_bindgen", features = ["wasm"] } 17 | ubrn_common = { path = "../crates/ubrn_common" } 18 | -------------------------------------------------------------------------------- /xtask/src/bootstrap/yarn.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use std::process::Command; 7 | 8 | use anyhow::Result; 9 | use camino::Utf8PathBuf; 10 | use clap::Args; 11 | use ubrn_common::{rm_dir, run_cmd}; 12 | 13 | use crate::util::repository_root; 14 | 15 | use super::Bootstrap; 16 | 17 | #[derive(Debug, Args)] 18 | pub(crate) struct YarnCmd; 19 | 20 | impl YarnCmd { 21 | pub(crate) fn node_modules() -> Result { 22 | let root = repository_root()?; 23 | Ok(root.join("node_modules")) 24 | } 25 | } 26 | 27 | impl Bootstrap for YarnCmd { 28 | fn marker() -> Result { 29 | Self::node_modules() 30 | } 31 | 32 | fn clean() -> Result<()> { 33 | rm_dir(Self::node_modules()?) 34 | } 35 | 36 | fn prepare(&self) -> Result<()> { 37 | let mut cmd = Command::new("yarn"); 38 | run_cmd( 39 | cmd.current_dir(repository_root()?) 40 | .arg("--frozen-lockfile") 41 | .arg("--emoji") 42 | .arg("true"), 43 | ) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /xtask/src/clean.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use anyhow::Result; 7 | use camino::Utf8Path; 8 | use clap::Args; 9 | use ubrn_common::{rm_dir, run_cmd}; 10 | 11 | use crate::{ 12 | bootstrap::BootstrapCmd, 13 | util::{build_root, cpp_modules, repository_root}, 14 | }; 15 | 16 | #[derive(Debug, Args)] 17 | pub(crate) struct CleanCmd; 18 | 19 | impl CleanCmd { 20 | pub(crate) fn run(&self) -> Result<()> { 21 | let root = repository_root()?; 22 | 23 | BootstrapCmd::clean_all()?; 24 | 25 | // run this last. 26 | rm_dir(build_root()?)?; 27 | rm_dir(cpp_modules()?)?; 28 | run_cargo_clean(&root)?; 29 | Ok(()) 30 | } 31 | } 32 | 33 | fn run_cargo_clean(dir: &Utf8Path) -> Result<()> { 34 | run_cmd( 35 | std::process::Command::new("cargo") 36 | .arg("clean") 37 | .current_dir(dir), 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /xtask/src/run/Cargo.template.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "my-test-crate" 3 | version = "0.1.0" 4 | authors = ["James Hugman "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [features] 11 | default = ["console_error_panic_hook"] 12 | 13 | [dependencies] 14 | # We want to ensure that the version of wasm-bindgen is selected by the 15 | # uniffi-runtime-javascript crate. 16 | # cargo is smart enough to do this if we don't put any further restrictions 17 | # on it. 18 | wasm-bindgen = "*" 19 | {{crate_name}} = { path = "{{crate_path}}" } 20 | uniffi-runtime-javascript = { path = "{{uniffi_runtime_javascript}}", features = ["wasm32"] } 21 | 22 | # The `console_error_panic_hook` crate provides better debugging of panics by 23 | # logging them with `console.error`. This is great for development, but requires 24 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 25 | # code size when deploying. 26 | console_error_panic_hook = { version = "0.1.7", optional = true } 27 | 28 | [dev-dependencies] 29 | wasm-bindgen-test = "0.3.34" 30 | 31 | [profile.release] 32 | # Tell `rustc` to optimize for small code size. 33 | opt-level = "s" 34 | 35 | [workspace] 36 | 37 | [workspace.dependencies] 38 | wasm-bindgen = "*" 39 | -------------------------------------------------------------------------------- /xtask/src/run/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | pub(crate) mod cpp_bindings; 7 | pub(crate) mod generate_bindings; 8 | pub(crate) mod jsi; 9 | pub(crate) mod nodejs; 10 | pub(crate) mod rust_crate; 11 | pub(crate) mod typescript; 12 | pub(crate) mod wasm; 13 | 14 | use anyhow::Result; 15 | use camino::Utf8PathBuf; 16 | use clap::Args; 17 | use generate_bindings::GenerateBindingsArg; 18 | use jsi::Jsi; 19 | use nodejs::NodeJs; 20 | use ubrn_bindgen::{AbiFlavor, SwitchArgs}; 21 | use wasm::Wasm; 22 | 23 | use self::{rust_crate::CrateArg, typescript::EntryArg}; 24 | 25 | #[derive(Debug, Args)] 26 | pub(crate) struct RunCmd { 27 | /// Clean the crate before starting. 28 | #[clap(long, short = 'c')] 29 | pub(crate) clean: bool, 30 | 31 | /// The crate to be bound to hermes 32 | #[command(flatten)] 33 | pub(crate) crate_: Option, 34 | 35 | #[clap(long = "cpp", conflicts_with_all = ["ts_dir", "abi_dir"])] 36 | pub(crate) cpp_binding: Option, 37 | 38 | #[clap(flatten)] 39 | pub(crate) generate_bindings: Option, 40 | 41 | #[clap(flatten)] 42 | pub(crate) switches: SwitchArgs, 43 | 44 | /// The Javascript or Typescript file. 45 | #[command(flatten)] 46 | pub(crate) js_file: EntryArg, 47 | } 48 | 49 | impl RunCmd { 50 | pub(crate) fn run(&self) -> Result<()> { 51 | match &self.switches.flavor { 52 | AbiFlavor::Jsi => Jsi.run(self), 53 | AbiFlavor::Wasm => Wasm.run(self), 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /xtask/src/run/nodejs.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | 7 | use std::process::Command; 8 | 9 | use anyhow::{Ok, Result}; 10 | use camino::Utf8Path; 11 | 12 | use crate::bootstrap::YarnCmd; 13 | 14 | use super::typescript::typecheck_ts; 15 | 16 | pub(crate) struct NodeJs; 17 | 18 | impl NodeJs { 19 | pub(crate) fn tsx(&self, file: &Utf8Path) -> Result<()> { 20 | typecheck_ts(file)?; 21 | let node_modules = YarnCmd::node_modules()?; 22 | let Some(tsx) = ubrn_common::find(node_modules, ".bin/tsx") else { 23 | unreachable!("Can't find tsx; this is likely a change in how tsx is packaged"); 24 | }; 25 | let mut cmd = Command::new(tsx); 26 | cmd.arg("--experimental-wasm-modules").arg(file); 27 | ubrn_common::run_cmd(&mut cmd)?; 28 | Ok(()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /xtask/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/ 5 | */ 6 | use anyhow::{Context, Result}; 7 | use camino::{Utf8Path, Utf8PathBuf}; 8 | use std::env; 9 | 10 | pub fn repository_root() -> Result { 11 | let dir = env::var("CARGO_MANIFEST_DIR").context("failed to get manifest dir")?; 12 | Ok(Utf8Path::new(&*dir).parent().unwrap().to_path_buf()) 13 | } 14 | 15 | pub fn build_root() -> Result { 16 | let dir = repository_root()?; 17 | Ok(dir.join("build")) 18 | } 19 | 20 | pub fn cpp_modules() -> Result { 21 | let dir = repository_root()?; 22 | Ok(dir.join("cpp_modules")) 23 | } 24 | --------------------------------------------------------------------------------