diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39e3832b..57b6d95f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,9 +9,6 @@ on: - cron: "0 6 * * 1-5" workflow_dispatch: -env: - CARGO_TERM_COLOR: always - jobs: build: name: Build on ${{ matrix.os }} @@ -23,43 +20,21 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: recursive - uses: actions/setup-java@v4 with: distribution: temurin java-version: 11 - - name: Install Rust toolchain - run: | - rustup show - rustup component add rustfmt clippy - - - name: Cargo Format - working-directory: zenoh-jni - run: cargo fmt --all --check - - - name: Clippy Check without Cargo.lock - working-directory: zenoh-jni - run: | - rm Cargo.lock - cargo clippy --all-targets --all-features -- -D warnings - git restore Cargo.lock - - - name: Check for feature leaks - working-directory: zenoh-jni - run: cargo test --no-default-features - - - name: Build Zenoh-JNI - working-directory: zenoh-jni - run: cargo build - - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 with: gradle-version: 8.12.1 - name: Gradle Test - run: gradle jvmTest --info + run: gradle jvmTest --info -Pzenoh.useLocalJniRuntime=true markdown_lint: runs-on: ubuntu-latest diff --git a/.github/workflows/publish-android.yml b/.github/workflows/publish-android.yml index f3317b25..b0a646f3 100644 --- a/.github/workflows/publish-android.yml +++ b/.github/workflows/publish-android.yml @@ -17,9 +17,6 @@ on: required: false default: true -env: - CARGO_TERM_COLOR: always - jobs: publish_android_package: name: Publish Android package @@ -37,25 +34,6 @@ jobs: distribution: temurin java-version: 11 - - uses: nttld/setup-ndk@v1 - id: setup-ndk - with: - ndk-version: r26 - add-to-path: false - link-to-sdk: true - - - name: Install Rust toolchain - run: | - rustup show - rustup component add rustfmt clippy - - - name: Setup Rust toolchains - run: | - rustup target add armv7-linux-androideabi - rustup target add i686-linux-android - rustup target add aarch64-linux-android - rustup target add x86_64-linux-android - - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 with: diff --git a/.github/workflows/publish-jvm.yml b/.github/workflows/publish-jvm.yml index b77ab842..9b961d8c 100644 --- a/.github/workflows/publish-jvm.yml +++ b/.github/workflows/publish-jvm.yml @@ -17,127 +17,9 @@ on: required: false default: true -env: - CARGO_TERM_COLOR: always - JNI_LIB_PATHS: jni-libs # Edit on the inner build.gradle.kts file as well. - jobs: - builds: - name: Build for ${{ matrix.job.target }} on ${{ matrix.job.os }} - if: ${{ !(github.event.inputs.build == 'false') }} - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - # In order to load any added target at runtime, editing the Zenoh class under jvmMain is required. - - { - target: x86_64-unknown-linux-gnu, - arch: amd64, - os: ubuntu-latest, - build-cmd: "cargo", - } - - { - target: aarch64-unknown-linux-gnu, - arch: arm64, - os: ubuntu-latest, - build-cmd: "cross", - } - - { - target: x86_64-apple-darwin, - arch: darwin, - os: macos-latest, - build-cmd: "cargo", - } - - { - target: aarch64-apple-darwin, - arch: darwin, - os: macos-latest, - build-cmd: "cargo", - } - - { - target: x86_64-pc-windows-msvc, - arch: win64, - os: windows-latest, - build-cmd: "cargo", - } - - { - target: aarch64-pc-windows-msvc, - arch: arm64, - os: windows-latest, - build-cmd: "cargo", - } - steps: - - name: Checkout source code - uses: actions/checkout@v4 - with: - ref: ${{ inputs.branch }} - - - name: Install prerequisites - shell: bash - run: | - case ${{ matrix.job.target }} in - *-linux-gnu*) cargo +stable install cargo-deb --locked ;; - esac - - case ${{ matrix.job.target }} in - aarch64-unknown-linux-gnu) - sudo apt-get -y update - sudo apt-get -y install gcc-aarch64-linux-gnu - ;; - esac - - cargo +stable install cross --locked - - - name: Install Rust toolchain - run: | - rustup show - rustup target add ${{ matrix.job.target }} - - - name: Build - run: ${{ matrix.job.build-cmd }} build --release --bins --lib --features=${{ github.event.inputs.features}} --target=${{ matrix.job.target }} --manifest-path zenoh-jni/Cargo.toml - - - name: Packaging - id: package - shell: bash - run: | - TARGET=${{ matrix.job.target }} - MAIN_PKG_NAME="${GITHUB_WORKSPACE}/${TARGET}.zip" - - case ${TARGET} in - *linux*) - cd "zenoh-jni/target/${TARGET}/release/" - echo "Packaging ${MAIN_PKG_NAME}:" - zip ${MAIN_PKG_NAME} libzenoh_jni.so - cd - - echo "MAIN_PKG_NAME=${MAIN_PKG_NAME}" >> $GITHUB_OUTPUT - ;; - *apple*) - cd "zenoh-jni/target/${TARGET}/release/" - echo "Packaging ${MAIN_PKG_NAME}:" - zip ${MAIN_PKG_NAME} libzenoh_jni.dylib - cd - - echo "MAIN_PKG_NAME=${MAIN_PKG_NAME}" >> $GITHUB_OUTPUT - ;; - *windows*) - cd "zenoh-jni/target/${TARGET}/release/" - echo "Packaging ${MAIN_PKG_NAME}:" - 7z -y a "${MAIN_PKG_NAME}" zenoh_jni.dll - cd - - echo "MAIN_PKG_NAME=${MAIN_PKG_NAME}" >> $GITHUB_OUTPUT - ;; - esac - - - name: "Upload packages" - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.job.target }} - path: | - ${{ steps.package.outputs.MAIN_PKG_NAME }} - publish_jvm_package: name: Publish JVM package - needs: builds permissions: contents: read packages: write @@ -148,14 +30,6 @@ jobs: with: ref: ${{ inputs.branch }} - - name: Create resources destination - run: mkdir ${{env.JNI_LIB_PATHS}} - - - name: Download result of previous builds - uses: actions/download-artifact@v4 - with: - path: ${{env.JNI_LIB_PATHS}} - - uses: actions/setup-java@v4 with: distribution: temurin diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e874c778..fa5b5906 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -106,15 +106,3 @@ jobs: with: live-run: ${{ inputs.live-run || false }} branch: ${{ needs.tag.outputs.branch }} - - publish-github: - needs: [tag, publish-android, publish-jvm] - runs-on: macos-latest - steps: - - uses: eclipse-zenoh/ci/publish-crates-github@main - with: - repo: ${{ github.repository }} - live-run: ${{ inputs.live-run || false }} - version: ${{ needs.tag.outputs.version }} - branch: ${{ needs.tag.outputs.branch }} - github-token: ${{ secrets.BOT_TOKEN_WORKFLOW }} diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..bc8a07b6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "zenoh-java"] + path = zenoh-java + url = https://github.com/eclipse-zenoh/zenoh-java.git + branch = common-jni diff --git a/README.md b/README.md index 6b5ee063..468254fa 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,6 @@ For the moment, the library targets the following platforms: Basically: -- Rust ([Installation guide](https://doc.rust-lang.org/cargo/getting-started/installation.html)) - Kotlin ([Installation guide](https://kotlinlang.org/docs/getting-started.html#backend)) - Gradle ([Installation guide](https://gradle.org/install/)) @@ -126,6 +125,8 @@ and in case of targetting Android you'll also need: - Android SDK ([Installation guide](https://developer.android.com/about/versions/11/setup-sdk)) +> **Note:** zenoh-kotlin no longer builds its own native JNI library. The native runtime is provided by [zenoh-jni-runtime](https://github.com/eclipse-zenoh/zenoh-java), which is a published Maven artifact that zenoh-kotlin depends on automatically. No Rust toolchain is required to build or publish zenoh-kotlin using the default (Maven) path. A Rust toolchain is only needed when using the [local submodule path](#running-the-tests) with `-Pzenoh.useLocalJniRuntime=true`. + ## JVM JVM To publish a library for a JVM project into Maven local, run @@ -134,11 +135,7 @@ To publish a library for a JVM project into Maven local, run gradle publishJvmPublicationToMavenLocal ``` -This will first, trigger the compilation of Zenoh-JNI, and second publish the library into maven local, containing the native library -as a resource that will be loaded during runtime. - -:warning: The native library will be compiled against the default rustup target on your machine, so although it may work fine -for you on your desktop, the generated publication may not be working on another computer with a different operating system and/or a different cpu architecture. +This publishes the zenoh-kotlin library to Maven local. The published artifact declares a dependency on `zenoh-jni-runtime`, which provides the native JNI binaries and is published separately by the [zenoh-java](https://github.com/eclipse-zenoh/zenoh-java) project. Once we have published the package, we should be able to find it under `~/.m2/repository/org/eclipse/zenoh/zenoh-kotlin-jvm/1.1.1`. @@ -157,40 +154,13 @@ dependencies { ## Android Android -In order to use these bindings in a native Android project, what we will do is to build them as an Android NDK Library, -publishing it into Maven local for us to be able to easily import it in our project. - -It is required to have the [NDK (native development kit)](https://developer.android.com/ndk) installed, since we are going to compile Zenoh JNI for multiple -android native targets. The currently used NDK version is **26.0.10792818**. -It can be set up by using Android Studio (go to `Preferences > Languages & Frameworks > Android SDK > SDK Tools`, tick `Show Package Details` and pick the right NDK version), -or alternatively it can be found [here](https://developer.android.com/ndk/downloads). - -The native platforms we are going to target are the following ones: - -- x86 -- x86_64 -- arm -- arm64 - -Therefore, if they are not yet already added to the Rust toolchain, run: - -```bash -rustup target add armv7-linux-androideabi; \ -rustup target add i686-linux-android; \ -rustup target add aarch64-linux-android; \ -rustup target add x86_64-linux-android -``` - -to install them. - -So, in order to publish the library onto Maven Local, run: +In order to use these bindings in a native Android project, publish them into Maven local: ```bash gradle -Pandroid=true publishAndroidReleasePublicationToMavenLocal ``` -This will first trigger the compilation of the Zenoh-JNI for the previously mentioned targets, and secondly will -publish the library, containing the native binaries. +This publishes the zenoh-kotlin-android artifact to Maven local. The published artifact declares a dependency on `zenoh-jni-runtime`, which provides the prebuilt native JNI binaries for Android ABIs (x86, x86_64, arm, arm64). The native binaries are published separately by the [zenoh-java](https://github.com/eclipse-zenoh/zenoh-java) project — no Rust toolchain or NDK cross-compilation is required. You should now be able to see the package under `~/.m2/repository/org/eclipse/zenoh/zenoh-kotlin-android/1.1.1`. @@ -226,13 +196,25 @@ gradle dokkaGenerate ## Running the tests -To run the tests, run: +zenoh-kotlin supports two modes for providing the native JNI runtime during tests: + +### Default mode (published Maven artifact) + +By default, tests resolve `zenoh-jni-runtime` from Maven Central. No local submodule or Rust toolchain is needed: ```bash gradle jvmTest ``` -This will compile the native library on debug mode (if not already available) and run the tests afterward against the JVM target. +### Local submodule mode (opt-in) + +For local integration testing against the `zenoh-java` submodule (included under `zenoh-java/`), pass the `zenoh.useLocalJniRuntime` property. This substitutes the Maven dependency with a local composite build of the submodule: + +```bash +gradle jvmTest -Pzenoh.useLocalJniRuntime=true +``` + +> **Note:** The local submodule path builds `zenoh-jni-runtime` from source and requires a Rust toolchain (see [rustup.rs](https://rustup.rs)) as well as the Cargo toolchain configured for the target platform. The submodule's Gradle build handles the Rust compilation step automatically once the toolchain is installed. ## Logging diff --git a/build.gradle.kts b/build.gradle.kts index 6ea68a7d..6888c637 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,8 +18,7 @@ buildscript { } dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0") - classpath("org.mozilla.rust-android-gradle:plugin:0.9.6") - classpath("com.android.tools.build:gradle:7.4.2") +classpath("com.android.tools.build:gradle:7.4.2") classpath("com.gradleup.shadow:shadow-gradle-plugin:9.0.0-beta6") } } @@ -28,8 +27,7 @@ plugins { id("com.android.library") version "7.4.2" apply false id("org.jetbrains.kotlin.android") version "1.9.10" apply false id("org.jetbrains.kotlin.multiplatform") version "1.9.0" apply false - id("org.mozilla.rust-android-gradle.rust-android") version "0.9.6" apply false - id("org.jetbrains.dokka") version "2.0.0" apply false +id("org.jetbrains.dokka") version "2.0.0" apply false id("com.adarshr.test-logger") version "3.2.0" apply false kotlin("plugin.serialization") version "1.9.0" apply false id("io.github.gradle-nexus.publish-plugin") version "2.0.0" diff --git a/ci/scripts/bump-and-tag.bash b/ci/scripts/bump-and-tag.bash index 5b5f7ee4..d02e2e45 100644 --- a/ci/scripts/bump-and-tag.bash +++ b/ci/scripts/bump-and-tag.bash @@ -5,27 +5,11 @@ set -xeo pipefail readonly live_run=${LIVE_RUN:-false} # Release number readonly version=${VERSION:?input VERSION is required} -# Dependencies' pattern -readonly bump_deps_pattern=${BUMP_DEPS_PATTERN:-''} -# Dependencies' version -readonly bump_deps_version=${BUMP_DEPS_VERSION:-''} -# Dependencies' git branch -readonly bump_deps_branch=${BUMP_DEPS_BRANCH:-''} # Git actor name readonly git_user_name=${GIT_USER_NAME:?input GIT_USER_NAME is required} # Git actor email readonly git_user_email=${GIT_USER_EMAIL:?input GIT_USER_EMAIL is required} -cargo +stable install toml-cli - -# NOTE(fuzzypixelz): toml-cli doesn't yet support in-place modification -# See: https://github.com/gnprice/toml-cli?tab=readme-ov-file#writing-ish-toml-set -function toml_set_in_place() { - local tmp=$(mktemp) - toml set "$1" "$2" "$3" > "$tmp" - mv "$tmp" "$1" -} - export GIT_AUTHOR_NAME=$git_user_name export GIT_AUTHOR_EMAIL=$git_user_email export GIT_COMMITTER_NAME=$git_user_name @@ -33,32 +17,8 @@ export GIT_COMMITTER_EMAIL=$git_user_email # Bump Gradle project version printf '%s' "$version" > version.txt -# Propagate version change to zenoh-jni -toml_set_in_place zenoh-jni/Cargo.toml "package.version" "$version" - -git commit version.txt zenoh-jni/Cargo.toml -m "chore: Bump version to \`$version\`" -# Select all package dependencies that match $bump_deps_pattern and bump them to $bump_deps_version -if [[ "$bump_deps_pattern" != '' ]]; then - deps=$(toml get zenoh-jni/Cargo.toml dependencies | jq -r "keys[] | select(test(\"$bump_deps_pattern\"))") - for dep in $deps; do - if [[ -n $bump_deps_version ]]; then - toml_set_in_place zenoh-jni/Cargo.toml "dependencies.$dep.version" "$bump_deps_version" - fi - - if [[ -n $bump_deps_branch ]]; then - toml_set_in_place zenoh-jni/Cargo.toml "dependencies.$dep.branch" "$bump_deps_branch" - fi - done - # Update lockfile - cargo check --manifest-path zenoh-jni/Cargo.toml - - if [[ -n $bump_deps_version || -n $bump_deps_branch ]]; then - git commit zenoh-jni/Cargo.toml zenoh-jni/Cargo.lock -m "chore: Bump \`$bump_deps_pattern\` dependencies to \`$bump_deps_version\`" - else - echo "warn: no changes have been made to any dependencies matching $bump_deps_pattern" - fi -fi +git commit version.txt -m "chore: Bump version to \`$version\`" if [[ ${live_run} ]]; then git tag --force "$version" -m "v$version" diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 4013e0d2..a890df2f 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -80,19 +80,9 @@ tasks { examples.forEach { example -> register(example, JavaExec::class) { - dependsOn("CompileZenohJNI") description = "Run the $example example" mainClass.set("io.zenoh.${example}Kt") classpath(sourceSets["main"].runtimeClasspath) - val zenohPaths = "../zenoh-jni/target/release" - val defaultJvmArgs = arrayListOf("-Djava.library.path=$zenohPaths") - jvmArgs(defaultJvmArgs) } } } - -tasks.register("CompileZenohJNI") { - project.exec { - commandLine("cargo", "build", "--release", "--manifest-path", "../zenoh-jni/Cargo.toml") - } -} diff --git a/gradle.properties b/gradle.properties index 5c311496..1d7fb9c6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,12 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true + +# Version of zenoh-jni-runtime to depend on (published by zenoh-java). +# Override in local gradle.properties when you need a different runtime version. +zenohJniRuntimeVersion=1.9.0 + +# Set to true to substitute the zenoh-jni-runtime Maven dependency with the local +# zenoh-java submodule build (useful for local development and testing only). +# Do NOT enable this for publication — the released artifact must resolve against +# the already-published runtime owned by zenoh-java. +zenoh.useLocalJniRuntime=false diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 075062e5..00000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "1.93.0" diff --git a/settings.gradle.kts b/settings.gradle.kts index 6126e8ce..ed9b889c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,7 +23,23 @@ rootProject.name = "zenoh-kotlin" include(":zenoh-kotlin") include(":examples") -include(":zenoh-jni") + +// Include the local zenoh-java submodule only when explicitly requested via the +// zenoh.useLocalJniRuntime property (local dev/test only — not for publication). +val useLocalJniRuntime = settings.providers.gradleProperty("zenoh.useLocalJniRuntime") + .orNull?.toBoolean() == true +if (useLocalJniRuntime) { + require(file("zenoh-java/settings.gradle.kts").exists()) { + "zenoh.useLocalJniRuntime=true was requested but the zenoh-java submodule is not initialized. " + + "Run: git submodule update --init --recursive" + } + includeBuild("zenoh-java") { + dependencySubstitution { + substitute(module("org.eclipse.zenoh:zenoh-jni-runtime")) + .using(project(":zenoh-jni-runtime")) + } + } +} plugins { id("org.gradle.toolchains.foojay-resolver-convention") version("0.4.0") diff --git a/zenoh-java b/zenoh-java new file mode 160000 index 00000000..fc47b41f --- /dev/null +++ b/zenoh-java @@ -0,0 +1 @@ +Subproject commit fc47b41f40974af89af6a3885821228ca9c66427 diff --git a/zenoh-jni/Cargo.lock b/zenoh-jni/Cargo.lock deleted file mode 100644 index bd456c8a..00000000 --- a/zenoh-jni/Cargo.lock +++ /dev/null @@ -1,4406 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "getrandom 0.3.4", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android-logd-logger" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0483169d5fac0887f85c2fa8fecfe08669791712d8260de1a6ec30630a62932f" -dependencies = [ - "bytes", - "env_logger", - "lazy_static", - "libc", - "log", - "parking_lot", - "redox_syscall 0.4.1", - "thiserror 1.0.69", - "time", - "winapi", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "arc-swap" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" -dependencies = [ - "rustversion", -] - -[[package]] -name = "array-init" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" - -[[package]] -name = "asn1-rs" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror 2.0.18", - "time", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" -dependencies = [ - "serde_core", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "cc" -version = "1.2.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "num-traits", - "serde", - "windows-link", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clap" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "atty", - "bitflags 1.3.2", - "clap_lex", - "indexmap 1.9.3", - "strsim 0.10.0", - "termcolor", - "textwrap", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const_format" -version = "0.2.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "darling" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim 0.11.1", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "data-encoding" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "der-parser" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", - "serde_core", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "dirs" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.61.2", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "env_logger" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "fastbloom" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7f34442dbe69c60fe8eaf58a8cafff81a1f278816d8ab4db255b3bef4ac3c4" -dependencies = [ - "getrandom 0.3.4", - "libm", - "rand 0.9.2", - "siphasher", -] - -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "flume" -version = "0.10.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" -dependencies = [ - "futures-core", - "futures-sink", - "nanorand", - "pin-project", - "spin 0.9.8", -] - -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "futures-core", - "futures-sink", - "nanorand", - "spin 0.9.8", -] - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi 5.3.0", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" -dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", - "wasip2", - "wasip3", -] - -[[package]] -name = "git-version" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad568aa3db0fcbc81f2f116137f263d7304f512a1209b35b85150d3ef88ad19" -dependencies = [ - "git-version-macro", -] - -[[package]] -name = "git-version-macro" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash 0.1.5", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.2.0", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "humantime" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", -] - -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "generic-array", -] - -[[package]] -name = "ipnetwork" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" -dependencies = [ - "serde", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys 0.3.1", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" -dependencies = [ - "jni-sys 0.4.1", -] - -[[package]] -name = "jni-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" -dependencies = [ - "jni-sys-macros", -] - -[[package]] -name = "jni-sys-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" -dependencies = [ - "quote", - "syn 2.0.117", -] - -[[package]] -name = "js-sys" -version = "0.3.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - -[[package]] -name = "keccak" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "keyed-set" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d255a6b6ecd77bb93ce91de984d7039bff7503f500eb4851a1269732f22baf" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin 0.9.8", -] - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libc" -version = "0.2.184" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" - -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link", -] - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "libredox" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" -dependencies = [ - "libc", -] - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "lz4_flex" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8c72594ac26bfd34f2d99dfced2edfaddfe8a476e3ff2ca0eb293d925c4f83" -dependencies = [ - "twox-hash", -] - -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "nanorand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "no-std-net" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "nonempty-collections" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e216d0e8cf9d54fa66e5780f6e1d5dc96d1c1b3c25aeba3b6758548bcbbd8b9d" -dependencies = [ - "serde", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" -dependencies = [ - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-conv" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi 0.5.2", - "libc", -] - -[[package]] -name = "oid-registry" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" -dependencies = [ - "asn1-rs", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.18", - "smallvec", - "windows-link", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pem" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" -dependencies = [ - "base64", - "serde_core", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pest" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" -dependencies = [ - "memchr", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pest_meta" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" -dependencies = [ - "pest", - "sha2", -] - -[[package]] -name = "petgraph" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" -dependencies = [ - "fixedbitset", - "hashbrown 0.15.5", - "indexmap 2.13.1", - "serde", -] - -[[package]] -name = "phf" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" -dependencies = [ - "phf_macros", - "phf_shared", - "serde", -] - -[[package]] -name = "phf_generator" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" -dependencies = [ - "fastrand", - "phf_shared", -] - -[[package]] -name = "phf_macros" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "phf_shared" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pnet_base" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc190d4067df16af3aba49b3b74c469e611cad6314676eaf1157f31aa0fb2f7" -dependencies = [ - "no-std-net", -] - -[[package]] -name = "pnet_datalink" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79e70ec0be163102a332e1d2d5586d362ad76b01cec86f830241f2b6452a7b7" -dependencies = [ - "ipnetwork", - "libc", - "pnet_base", - "pnet_sys", - "winapi", -] - -[[package]] -name = "pnet_sys" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d4643d3d4db6b08741050c2f3afa9a892c4244c085a72fcda93c9c2c9a00f4b" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "portable-atomic" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2 0.6.3", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "bytes", - "fastbloom", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2 0.6.3", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rcgen" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b99e0098aa4082912d4c649628623db6aba77335e4f4569ff5083a6448b32e" -dependencies = [ - "pem", - "ring", - "rustls-pki-types", - "time", - "x509-parser", - "yasna", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "redox_users" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" -dependencies = [ - "getrandom 0.2.17", - "libredox", - "thiserror 2.0.18", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "ringbuffer-spsc" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3e7aa0a681b232e7cd7f856a53b10603df88ca74b79a8d8088845185492e35" -dependencies = [ - "array-init", - "crossbeam", -] - -[[package]] -name = "ron" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" -dependencies = [ - "bitflags 2.11.0", - "once_cell", - "serde", - "serde_derive", - "typeid", - "unicode-ident", -] - -[[package]] -name = "rsa" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustc-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - -[[package]] -name = "rustls" -version = "0.23.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" -dependencies = [ - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "schemars" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" -dependencies = [ - "dyn-clone", - "either", - "ref-cast", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.117", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "secrecy" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" -dependencies = [ - "serde", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.11.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_spanned" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_with" -version = "3.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" -dependencies = [ - "base64", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.13.1", - "schemars 0.9.0", - "schemars 1.2.1", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap 2.13.1", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2-const-stable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shellexpand" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8" -dependencies = [ - "dirs", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - -[[package]] -name = "simd-adler32" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - -[[package]] -name = "siphasher" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "socket2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stabby" -version = "72.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976399a0c48ea769ef7f5dc303bb88240ab8d84008647a6b2303eced3dab3945" -dependencies = [ - "rustversion", - "stabby-abi", -] - -[[package]] -name = "stabby-abi" -version = "72.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b54832a9a1f92a0e55e74a5c0332744426edc515bb3fbad82f10b874a87f0d" -dependencies = [ - "rustc_version", - "rustversion", - "sha2-const-stable", - "stabby-macros", -] - -[[package]] -name = "stabby-macros" -version = "72.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a768b1e51e4dbfa4fa52ae5c01241c0a41e2938fdffbb84add0c8238092f9091" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "rand 0.8.5", - "syn 1.0.109", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tls-listener" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1461056cc1ef47003f7ee16e4cef3741068d4c7f6b627bfce49b7c00c120a530" -dependencies = [ - "futures-util", - "pin-project-lite", - "thiserror 2.0.18", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "token-cell" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb48920ae769b58126c8c93269805011c793201f95fde28b479b81a9a531bbde" -dependencies = [ - "paste", - "portable-atomic", - "rustversion", -] - -[[package]] -name = "tokio" -version = "1.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2 0.6.3", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "futures-util", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.9.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" -dependencies = [ - "indexmap 2.13.1", - "serde_core", - "serde_spanned", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 0.7.15", -] - -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.25.11+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" -dependencies = [ - "indexmap 2.13.1", - "toml_datetime 1.1.1+spec-1.1.0", - "toml_parser", - "winnow 1.0.1", -] - -[[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" -dependencies = [ - "winnow 1.0.1", -] - -[[package]] -name = "toml_writer" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", - "tracing-serde", -] - -[[package]] -name = "tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.8.5", - "sha1", - "thiserror 1.0.69", - "utf-8", -] - -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "static_assertions", -] - -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - -[[package]] -name = "uhlc" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62a645e3e4e6c85b7abe49b086aa3204119431f42b6123b0070419fb6e9d24e" -dependencies = [ - "humantime", - "lazy_static", - "log", - "rand 0.8.5", - "serde", - "spin 0.10.0", -] - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "unzip-n" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b5bb2756c16fb66f80cfbf5fb0e0c09a7001e739f453c9ec241b9c8b1556fda" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" -dependencies = [ - "getrandom 0.4.2", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "validated_struct" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "869a93e8a7286e339e1128630051d82babbcd75d585975af07b9f3327220e60e" -dependencies = [ - "json5", - "serde", - "serde_json", - "validated_struct_macros", -] - -[[package]] -name = "validated_struct_macros" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c44ce98e7227a04eeb4cf9c784109a5c9710e54849ceb4f09f8597247897f1e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "unzip-n", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap 2.13.1", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.11.0", - "hashbrown 0.15.5", - "indexmap 2.13.1", - "semver", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webpki-roots" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" - -[[package]] -name = "winnow" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap 2.13.1", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.11.0", - "indexmap 2.13.1", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.13.1", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "x509-parser" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "ring", - "rusticata-macros", - "thiserror 2.0.18", - "time", -] - -[[package]] -name = "yasna" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" -dependencies = [ - "time", -] - -[[package]] -name = "yoke" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zenoh" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "ahash", - "arc-swap", - "async-trait", - "bytes", - "const_format", - "flate2", - "flume 0.11.1", - "futures", - "git-version", - "itertools", - "json5", - "lazy_static", - "nonempty-collections", - "once_cell", - "petgraph", - "phf", - "rand 0.8.5", - "rustc_version", - "serde", - "serde_json", - "socket2 0.5.10", - "tokio", - "tokio-util", - "tracing", - "uhlc", - "vec_map", - "zenoh-buffers", - "zenoh-codec", - "zenoh-collections", - "zenoh-config", - "zenoh-core", - "zenoh-keyexpr", - "zenoh-link", - "zenoh-link-commons", - "zenoh-macros", - "zenoh-plugin-trait", - "zenoh-protocol", - "zenoh-result", - "zenoh-runtime", - "zenoh-sync", - "zenoh-task", - "zenoh-transport", - "zenoh-util", -] - -[[package]] -name = "zenoh-buffers" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "zenoh-collections", -] - -[[package]] -name = "zenoh-codec" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "tracing", - "uhlc", - "zenoh-buffers", - "zenoh-protocol", -] - -[[package]] -name = "zenoh-collections" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "ahash", -] - -[[package]] -name = "zenoh-config" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "json5", - "nonempty-collections", - "num_cpus", - "secrecy", - "serde", - "serde_json", - "serde_with", - "serde_yaml", - "toml", - "tracing", - "uhlc", - "validated_struct", - "zenoh-core", - "zenoh-keyexpr", - "zenoh-macros", - "zenoh-protocol", - "zenoh-result", - "zenoh-util", -] - -[[package]] -name = "zenoh-core" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "lazy_static", - "tokio", - "zenoh-result", - "zenoh-runtime", -] - -[[package]] -name = "zenoh-crypto" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "aes", - "hmac", - "rand 0.8.5", - "rand_chacha 0.3.1", - "sha3", - "zenoh-result", -] - -[[package]] -name = "zenoh-ext" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "async-trait", - "bincode", - "flume 0.11.1", - "futures", - "leb128", - "serde", - "tokio", - "tracing", - "uhlc", - "zenoh", - "zenoh-macros", - "zenoh-util", -] - -[[package]] -name = "zenoh-keyexpr" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "getrandom 0.2.17", - "hashbrown 0.16.1", - "keyed-set", - "rand 0.8.5", - "schemars 1.2.1", - "serde", - "token-cell", - "zenoh-result", -] - -[[package]] -name = "zenoh-link" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "zenoh-config", - "zenoh-link-commons", - "zenoh-link-quic", - "zenoh-link-quic_datagram", - "zenoh-link-tcp", - "zenoh-link-tls", - "zenoh-link-udp", - "zenoh-link-unixsock_stream", - "zenoh-link-ws", - "zenoh-protocol", - "zenoh-result", -] - -[[package]] -name = "zenoh-link-commons" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "async-trait", - "base64", - "bytes", - "flume 0.11.1", - "futures", - "quinn", - "quinn-proto", - "rcgen", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "rustls-webpki", - "secrecy", - "serde", - "socket2 0.5.10", - "time", - "tokio", - "tokio-util", - "tracing", - "webpki-roots", - "x509-parser", - "zenoh-buffers", - "zenoh-codec", - "zenoh-config", - "zenoh-core", - "zenoh-protocol", - "zenoh-result", - "zenoh-runtime", - "zenoh-util", -] - -[[package]] -name = "zenoh-link-quic" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "async-trait", - "rustls-webpki", - "time", - "tracing", - "zenoh-core", - "zenoh-link-commons", - "zenoh-link-quic_datagram", - "zenoh-protocol", - "zenoh-result", -] - -[[package]] -name = "zenoh-link-quic_datagram" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "async-trait", - "rustls-webpki", - "time", - "tokio-util", - "tracing", - "zenoh-core", - "zenoh-link-commons", - "zenoh-protocol", - "zenoh-result", -] - -[[package]] -name = "zenoh-link-tcp" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "async-trait", - "socket2 0.5.10", - "tokio", - "tokio-util", - "tracing", - "zenoh-config", - "zenoh-core", - "zenoh-link-commons", - "zenoh-protocol", - "zenoh-result", -] - -[[package]] -name = "zenoh-link-tls" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "async-trait", - "base64", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "rustls-webpki", - "secrecy", - "socket2 0.5.10", - "time", - "tls-listener", - "tokio", - "tokio-rustls", - "tokio-util", - "tracing", - "webpki-roots", - "x509-parser", - "zenoh-config", - "zenoh-core", - "zenoh-link-commons", - "zenoh-protocol", - "zenoh-result", - "zenoh-runtime", -] - -[[package]] -name = "zenoh-link-udp" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "async-trait", - "libc", - "socket2 0.5.10", - "tokio", - "tokio-util", - "tracing", - "windows-sys 0.61.2", - "zenoh-buffers", - "zenoh-core", - "zenoh-link-commons", - "zenoh-link-quic_datagram", - "zenoh-protocol", - "zenoh-result", - "zenoh-sync", - "zenoh-util", -] - -[[package]] -name = "zenoh-link-unixsock_stream" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "async-trait", - "nix", - "tokio", - "tokio-util", - "tracing", - "uuid", - "zenoh-core", - "zenoh-link-commons", - "zenoh-protocol", - "zenoh-result", - "zenoh-runtime", -] - -[[package]] -name = "zenoh-link-ws" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "async-trait", - "futures-util", - "tokio", - "tokio-tungstenite", - "tokio-util", - "tracing", - "url", - "zenoh-core", - "zenoh-link-commons", - "zenoh-protocol", - "zenoh-result", - "zenoh-runtime", - "zenoh-util", -] - -[[package]] -name = "zenoh-macros" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "zenoh-keyexpr", -] - -[[package]] -name = "zenoh-plugin-trait" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "git-version", - "libloading", - "serde", - "stabby", - "tracing", - "zenoh-config", - "zenoh-keyexpr", - "zenoh-macros", - "zenoh-result", - "zenoh-util", -] - -[[package]] -name = "zenoh-protocol" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "const_format", - "rand 0.8.5", - "serde", - "uhlc", - "zenoh-buffers", - "zenoh-keyexpr", - "zenoh-macros", - "zenoh-result", -] - -[[package]] -name = "zenoh-result" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "anyhow", -] - -[[package]] -name = "zenoh-runtime" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "lazy_static", - "ron", - "serde", - "tokio", - "tracing", - "zenoh-macros", - "zenoh-result", -] - -[[package]] -name = "zenoh-sync" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "arc-swap", - "event-listener", - "futures", - "tokio", - "zenoh-buffers", - "zenoh-collections", - "zenoh-core", -] - -[[package]] -name = "zenoh-task" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "futures", - "tokio", - "tokio-util", - "tracing", - "zenoh-core", - "zenoh-runtime", -] - -[[package]] -name = "zenoh-transport" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "async-trait", - "crossbeam-utils", - "flume 0.11.1", - "futures", - "lazy_static", - "lz4_flex", - "rand 0.8.5", - "ringbuffer-spsc", - "rsa", - "serde", - "sha3", - "tokio", - "tokio-util", - "tracing", - "zenoh-buffers", - "zenoh-codec", - "zenoh-config", - "zenoh-core", - "zenoh-crypto", - "zenoh-link", - "zenoh-link-commons", - "zenoh-protocol", - "zenoh-result", - "zenoh-runtime", - "zenoh-sync", - "zenoh-task", - "zenoh-util", -] - -[[package]] -name = "zenoh-util" -version = "1.9.0" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#ec8b19786aced876d9dd64891359d6b2500d9a9b" -dependencies = [ - "async-trait", - "const_format", - "flume 0.11.1", - "home", - "humantime", - "lazy_static", - "libc", - "libloading", - "pnet_datalink", - "schemars 1.2.1", - "serde", - "serde_json", - "shellexpand", - "tokio", - "tracing", - "tracing-subscriber", - "winapi", - "zenoh-core", - "zenoh-result", -] - -[[package]] -name = "zenoh_jni" -version = "1.9.0" -dependencies = [ - "android-logd-logger", - "async-std", - "clap", - "flume 0.10.14", - "jni", - "json5", - "rustc_version", - "serde_yaml", - "tracing", - "uhlc", - "zenoh", - "zenoh-ext", -] - -[[package]] -name = "zerocopy" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerofrom" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/zenoh-jni/Cargo.toml b/zenoh-jni/Cargo.toml deleted file mode 100644 index 9e0b70dd..00000000 --- a/zenoh-jni/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -# -# Copyright (c) 2023 ZettaScale Technology -# -# This program and the accompanying materials are made available under the -# terms of the Eclipse Public License 2.0 which is available at -# http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -# which is available at https://www.apache.org/licenses/LICENSE-2.0. -# -# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -# -# Contributors: -# ZettaScale Zenoh Team, -# - -[package] -rust-version = "1.75.0" -version = "1.9.0" -repository = "https://github.com/eclipse-zenoh/zenoh" -homepage = "http://zenoh.io" -edition = "2021" -license = "EPL-2.0 OR Apache-2.0" -categories = ["network-programming"] -description = "Zenoh: Zero Overhead Pub/sub, Store/Query and Compute." -name = "zenoh_jni" - -[features] -default = ["zenoh/default", "zenoh-ext"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -android-logd-logger = "0.4.0" -async-std = { version = "=1.12.0", default-features = false } -clap = "3.2.23" -jni = "0.21.1" -flume = "0.10.14" -uhlc = "0.8.0" -json5 = "0.4.1" -serde_yaml = "0.9.19" -zenoh = { version = "1.9.0", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", features = ["unstable", "internal"], default-features = false } -zenoh-ext = { version = "1.9.0", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", features = ["unstable", "internal"], default-features = false, optional = true } -tracing = { version = "0.1" , features = ["log"] } - -[lib] -name = "zenoh_jni" -crate-type = ["staticlib", "dylib"] - -[build-dependencies] -rustc_version = "0.4.0" - -[profile.release] -debug = false # If you want debug symbol in release mode, set the env variable: RUSTFLAGS=-g -lto = "fat" -codegen-units = 1 -opt-level = 3 -panic = "abort" diff --git a/zenoh-jni/src/config.rs b/zenoh-jni/src/config.rs deleted file mode 100644 index 0ada1340..00000000 --- a/zenoh-jni/src/config.rs +++ /dev/null @@ -1,185 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::{ptr::null, sync::Arc}; - -use jni::{ - objects::{JClass, JString}, - sys::jstring, - JNIEnv, -}; -use zenoh::Config; - -use crate::{errors::ZResult, zerror}; -use crate::{throw_exception, utils::decode_string}; - -/// Loads the default configuration, returning a raw pointer to it. -/// -/// The pointer to the config is expected to be freed later on upon the destruction of the -/// Kotlin Config instance. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNIConfig_00024Companion_loadDefaultConfigViaJNI( - _env: JNIEnv, - _class: JClass, -) -> *const Config { - let config = Config::default(); - Arc::into_raw(Arc::new(config)) -} - -/// Loads the config from a file, returning a pointer to the loaded config in case of success. -/// In case of failure, an exception is thrown via JNI. -/// -/// The pointer to the config is expected to be freed later on upon the destruction of the -/// Kotlin Config instance. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNIConfig_00024Companion_loadConfigFileViaJNI( - mut env: JNIEnv, - _class: JClass, - config_path: JString, -) -> *const Config { - || -> ZResult<*const Config> { - let config_file_path = decode_string(&mut env, &config_path)?; - let config = Config::from_file(config_file_path).map_err(|err| zerror!(err))?; - Ok(Arc::into_raw(Arc::new(config))) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }) -} - -/// Loads the config from a json/json5 formatted string, returning a pointer to the loaded config -/// in case of success. In case of failure, an exception is thrown via JNI. -/// -/// The pointer to the config is expected to be freed later on upon the destruction of the -/// Kotlin Config instance. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNIConfig_00024Companion_loadJsonConfigViaJNI( - mut env: JNIEnv, - _class: JClass, - json_config: JString, -) -> *const Config { - || -> ZResult<*const Config> { - let json_config = decode_string(&mut env, &json_config)?; - let mut deserializer = - json5::Deserializer::from_str(&json_config).map_err(|err| zerror!(err))?; - let config = Config::from_deserializer(&mut deserializer).map_err(|err| match err { - Ok(c) => zerror!("Invalid configuration: {}", c), - Err(e) => zerror!("JSON error: {}", e), - })?; - Ok(Arc::into_raw(Arc::new(config))) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }) -} - -/// Loads the config from a yaml-formatted string, returning a pointer to the loaded config -/// in case of success. In case of failure, an exception is thrown via JNI. -/// -/// The pointer to the config is expected to be freed later on upon the destruction of the -/// Kotlin Config instance. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNIConfig_00024Companion_loadYamlConfigViaJNI( - mut env: JNIEnv, - _class: JClass, - yaml_config: JString, -) -> *const Config { - || -> ZResult<*const Config> { - let yaml_config = decode_string(&mut env, &yaml_config)?; - let deserializer = serde_yaml::Deserializer::from_str(&yaml_config); - let config = Config::from_deserializer(deserializer).map_err(|err| match err { - Ok(c) => zerror!("Invalid configuration: {}", c), - Err(e) => zerror!("YAML error: {}", e), - })?; - Ok(Arc::into_raw(Arc::new(config))) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }) -} - -/// Returns the json value associated to the provided [key]. May throw an exception in case of failure, which must be handled -/// on the kotlin layer. -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIConfig_00024Companion_getJsonViaJNI( - mut env: JNIEnv, - _class: JClass, - cfg_ptr: *const Config, - key: JString, -) -> jstring { - let arc_cfg: Arc = Arc::from_raw(cfg_ptr); - let result = || -> ZResult { - let key = decode_string(&mut env, &key)?; - let json = arc_cfg.get_json(&key).map_err(|err| zerror!(err))?; - let java_json = env.new_string(json).map_err(|err| zerror!(err))?; - Ok(java_json.as_raw()) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - JString::default().as_raw() - }); - std::mem::forget(arc_cfg); - result -} - -/// Inserts a json5 value associated to the provided [key]. May throw an exception in case of failure, which must be handled -/// on the kotlin layer. -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIConfig_00024Companion_insertJson5ViaJNI( - mut env: JNIEnv, - _class: JClass, - cfg_ptr: *const Config, - key: JString, - value: JString, -) { - || -> ZResult<()> { - let key = decode_string(&mut env, &key)?; - let value = decode_string(&mut env, &value)?; - let mut config = core::ptr::read(cfg_ptr); - let insert_result = config - .insert_json5(&key, &value) - .map_err(|err| zerror!(err)); - core::ptr::write(cfg_ptr as *mut _, config); - insert_result - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - }) -} - -/// Frees the pointer to the config. The pointer should be valid and should have been obtained through -/// one of the preceding `load` functions. This function should be called upon destruction of the kotlin -/// Config instance. -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIConfig_00024Companion_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - config_ptr: *const Config, -) { - Arc::from_raw(config_ptr); -} diff --git a/zenoh-jni/src/errors.rs b/zenoh-jni/src/errors.rs deleted file mode 100644 index 23687d4c..00000000 --- a/zenoh-jni/src/errors.rs +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::fmt; - -use jni::JNIEnv; - -#[macro_export] -macro_rules! throw_exception { - ($env:expr, $err:expr) => {{ - let _ = $err.throw_on_jvm(&mut $env).map_err(|err| { - tracing::error!("Unable to throw exception: {}", err); - }); - }}; -} - -#[macro_export] -macro_rules! zerror { - ($arg:expr) => { - $crate::errors::ZError($arg.to_string()) - }; - ($fmt:expr, $($arg:tt)*) => { - $crate::errors::ZError(format!($fmt, $($arg)*)) - }; -} - -pub(crate) type ZResult = core::result::Result; - -#[derive(Debug)] -pub(crate) struct ZError(pub String); - -impl fmt::Display for ZError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl ZError { - const KOTLIN_EXCEPTION_NAME: &'static str = "io/zenoh/exceptions/ZError"; - - pub fn throw_on_jvm(&self, env: &mut JNIEnv) -> ZResult<()> { - let exception_class = env - .find_class(Self::KOTLIN_EXCEPTION_NAME) - .map_err(|err| zerror!("Failed to retrieve exception class: {}", err))?; - env.throw_new(exception_class, self.to_string()) - .map_err(|err| zerror!("Failed to throw exception: {}", err)) - } -} diff --git a/zenoh-jni/src/ext/advanced_publisher.rs b/zenoh-jni/src/ext/advanced_publisher.rs deleted file mode 100644 index d0a51e36..00000000 --- a/zenoh-jni/src/ext/advanced_publisher.rs +++ /dev/null @@ -1,339 +0,0 @@ -// -// Copyright (c) 2025 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use jni::objects::JValue; -use jni::{ - objects::{JByteArray, JClass, JString}, - sys::jint, - JNIEnv, -}; -use zenoh::handlers::{Callback, DefaultHandler}; -use zenoh::Wait; -use zenoh_ext::AdvancedPublisher; - -use crate::owned_object::OwnedObject; -use crate::utils::{get_callback_global_ref, get_java_vm, load_on_close}; - -use crate::throw_exception; -use crate::{ - errors::ZResult, - utils::{decode_byte_array, decode_encoding}, - zerror, -}; -use jni::sys::jboolean; -use std::ptr::null; - -use jni::objects::JObject; -use zenoh::matching::{MatchingListener, MatchingListenerBuilder, MatchingStatus}; - -trait SetJniMatchingStatusCallback { - type WithCallback; - - unsafe fn set_jni_matching_status_callback( - self, - env: &mut JNIEnv, - callback: JObject, - on_close: JObject, - ) -> ZResult; -} - -impl<'a> SetJniMatchingStatusCallback for MatchingListenerBuilder<'a, DefaultHandler> { - type WithCallback = MatchingListenerBuilder<'a, Callback>; - - unsafe fn set_jni_matching_status_callback( - self, - env: &mut JNIEnv, - callback: JObject, - on_close: JObject, - ) -> ZResult { - let java_vm = Arc::new(get_java_vm(env)?); - let callback_global_ref = get_callback_global_ref(env, callback)?; - let on_close_global_ref = get_callback_global_ref(env, on_close)?; - let on_close = load_on_close(&java_vm, on_close_global_ref); - - let builder = self.callback(move |matching_status| { - on_close.noop(); // Moves `on_close` inside the closure so it gets destroyed with the closure - let _ = || -> ZResult<()> { - let mut env = java_vm.attach_current_thread_as_daemon().map_err(|err| { - zerror!("Unable to attach thread for matching listener: {}", err) - })?; - - env.call_method( - &callback_global_ref, - "run", - "(Z)V", - &[JValue::from(matching_status.matching())], - ) - .map_err(|err| zerror!(err))?; - Ok(()) - }() - .map_err(|err| tracing::error!("On matching listener callback error: {err}")); - }); - Ok(builder) - } -} - -/// Declare a MatchingListener for [AdvancedPublisher] via JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `advanced_publisher_ptr`: The raw pointer to an [AdvancedPublisher]. -/// - `callback`: The callback function as an instance of the `JNIMatchingListenerCallback` interface in Java/Kotlin. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called upon undeclaring the [MatchingListener]. -/// -/// Returns: -/// - A raw pointer to the declared [MatchingListener]. In case of failure, an exception is thrown and null is returned. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided [AdvancedPublisher] pointer is valid and has not been modified or freed. -/// - The [AdvancedPublisher] pointer remains valid and the ownership of the [AdvancedPublisher] is not transferred, -/// allowing safe usage of the [AdvancedPublisher] after this function call. -/// - The callback function passed as `callback` must be a valid instance of the `JNIMatchingListenerCallback` interface -/// in Java/Kotlin, matching the specified signature. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -#[cfg(feature = "zenoh-ext")] -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIAdvancedPublisher_declareMatchingListenerViaJNI( - mut env: JNIEnv, - _class: JClass, - advanced_publisher_ptr: *const AdvancedPublisher, - - callback: JObject, - on_close: JObject, -) -> *const MatchingListener<()> { - let advanced_publisher = OwnedObject::from_raw(advanced_publisher_ptr); - - || -> ZResult<*const MatchingListener<()>> { - tracing::debug!( - "Declaring matching listener on '{}'...", - advanced_publisher.key_expr() - ); - - let matching_listener = advanced_publisher - .matching_listener() - .set_jni_matching_status_callback(&mut env, callback, on_close)? - .wait() - .map_err(|err| zerror!("Unable to declare matching listener: {}", err))?; - - tracing::debug!( - "Matching listener declared on '{}'...", - advanced_publisher.key_expr() - ); - Ok(Arc::into_raw(Arc::new(matching_listener))) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }) -} - -/// Declare a background matching listener for [AdvancedPublisher] via JNI. -/// Register the listener callback to be run in background until the [AdvancedPublisher] is undeclared. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `advanced_publisher_ptr`: The raw pointer to an [AdvancedPublisher]. -/// - `callback`: The callback function as an instance of the `JNIMatchingListenerCallback` interface in Java/Kotlin. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called upon undeclaring the [AdvancedPublisher]. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided [AdvancedPublisher] pointer is valid and has not been modified or freed. -/// - The [AdvancedPublisher] pointer remains valid and the ownership of the [AdvancedPublisher] is not transferred, -/// allowing safe usage of the [AdvancedPublisher] after this function call. -/// - The callback function passed as `callback` must be a valid instance of the `JNIMatchingListenerCallback` interface -/// in Java/Kotlin, matching the specified signature. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -#[cfg(feature = "zenoh-ext")] -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIAdvancedPublisher_declareBackgroundMatchingListenerViaJNI( - mut env: JNIEnv, - _class: JClass, - advanced_publisher_ptr: *const AdvancedPublisher, - - callback: JObject, - on_close: JObject, -) { - let advanced_publisher = OwnedObject::from_raw(advanced_publisher_ptr); - - || -> ZResult<()> { - tracing::debug!( - "Declaring background matching listener on '{}'...", - advanced_publisher.key_expr() - ); - - advanced_publisher - .matching_listener() - .set_jni_matching_status_callback(&mut env, callback, on_close)? - .background() - .wait() - .map_err(|err| zerror!("Unable to declare background matching listener: {}", err))?; - - tracing::debug!( - "Background matching listener declared on '{}'...", - advanced_publisher.key_expr() - ); - Ok(()) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - }); -} - -/// Return the matching status of the [AdvancedPublisher]. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `advanced_publisher_ptr`: The raw pointer to an [AdvancedPublisher]. -/// -/// Returns: -/// - will return true if there exist Subscribers matching the Publisher's key expression and false otherwise. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided [AdvancedPublisher] pointer is valid and has not been modified or freed. -/// - The [AdvancedPublisher] pointer remains valid and the ownership of the [AdvancedPublisher] is not transferred, -/// allowing safe usage of the [AdvancedPublisher] after this function call. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -#[cfg(feature = "zenoh-ext")] -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIAdvancedPublisher_getMatchingStatusViaJNI( - mut env: JNIEnv, - _class: JClass, - advanced_publisher_ptr: *const AdvancedPublisher, -) -> jboolean { - use crate::errors::ZError; - - let advanced_publisher = OwnedObject::from_raw(advanced_publisher_ptr); - advanced_publisher - .matching_status() - .wait() - .map(|val| val.matching() as jboolean) - .map_err(|e| zerror!(e.to_string())) - .unwrap_or_else(|err: ZError| { - throw_exception!(env, err); - false as jboolean - }) -} - -/// Performs a PUT operation on an [AdvancedPublisher] via JNI. -/// -/// # Parameters -/// - `env`: The JNI environment pointer. -/// - `_class`: The Java class reference (unused). -/// - `payload`: The byte array to be published. -/// - `encoding_id`: The encoding ID of the payload. -/// - `encoding_schema`: Nullable encoding schema string of the payload. -/// - `attachment`: Nullble byte array for the attachment. -/// - `publisher_ptr`: The raw pointer to the [AdvancedPublisher]. -/// -/// # Safety -/// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - Assumes that the provided [AdvancedPublisher] pointer is valid and has not been modified or freed. -/// - The [AdvancedPublisher] pointer remains valid after this function call. -/// - May throw an exception in case of failure, which must be handled by the caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIAdvancedPublisher_putViaJNI( - mut env: JNIEnv, - _class: JClass, - payload: JByteArray, - encoding_id: jint, - encoding_schema: /*nullable*/ JString, - attachment: /*nullable*/ JByteArray, - publisher_ptr: *const AdvancedPublisher<'static>, -) { - let publisher = OwnedObject::from_raw(publisher_ptr); - let _ = || -> ZResult<()> { - let payload = decode_byte_array(&env, payload)?; - let mut publication = publisher.put(payload); - let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; - publication = publication.encoding(encoding); - if !attachment.is_null() { - let attachment = decode_byte_array(&env, attachment)?; - publication = publication.attachment::>(attachment) - }; - publication.wait().map_err(|err| zerror!(err)) - }() - .map_err(|err| throw_exception!(env, err)); -} - -/// Performs a DELETE operation on an [AdvancedPublisher] via JNI. -/// -/// # Parameters -/// - `env`: The JNI environment pointer. -/// - `_class`: The Java class reference (unused). -/// - `attachment`: Nullble byte array for the attachment. -/// - `publisher_ptr`: The raw pointer to the [AdvancedPublisher]. -/// -/// # Safety -/// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - Assumes that the provided [AdvancedPublisher] pointer is valid and has not been modified or freed. -/// - The [AdvancedPublisher] pointer remains valid after this function call. -/// - May throw an exception in case of failure, which must be handled by the caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIAdvancedPublisher_deleteViaJNI( - mut env: JNIEnv, - _class: JClass, - attachment: /*nullable*/ JByteArray, - publisher_ptr: *const AdvancedPublisher<'static>, -) { - let publisher = OwnedObject::from_raw(publisher_ptr); - let _ = || -> ZResult<()> { - let mut delete = publisher.delete(); - if !attachment.is_null() { - let attachment = decode_byte_array(&env, attachment)?; - delete = delete.attachment::>(attachment) - }; - delete.wait().map_err(|err| zerror!(err)) - }() - .map_err(|err| throw_exception!(env, err)); -} - -/// Frees the [AdvancedPublisher]. -/// -/// # Parameters: -/// - `_env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `publisher_ptr`: The raw pointer to the [AdvancedPublisher]. -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation. -/// - It assumes that the provided [AdvancedPublisher] pointer is valid and has not been modified or freed. -/// - After calling this function, the [AdvancedPublisher] pointer becomes invalid and should not be used anymore. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIAdvancedPublisher_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - publisher_ptr: *const AdvancedPublisher, -) { - Arc::from_raw(publisher_ptr); -} diff --git a/zenoh-jni/src/ext/advanced_subscriber.rs b/zenoh-jni/src/ext/advanced_subscriber.rs deleted file mode 100644 index 532c84e8..00000000 --- a/zenoh-jni/src/ext/advanced_subscriber.rs +++ /dev/null @@ -1,359 +0,0 @@ -// -// Copyright (c) 2025 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use jni::sys::jboolean; -use jni::{objects::JClass, JNIEnv}; -use zenoh::handlers::{Callback, DefaultHandler}; -use zenoh::pubsub::Subscriber; -use zenoh_ext::SampleMissListener; -use zenoh_ext::{AdvancedSubscriber, Miss, SampleMissListenerBuilder}; - -use crate::sample_callback::SetJniSampleCallback; -use jni::objects::JObject; - -use crate::errors::ZResult; -use jni::objects::JValue; -use zenoh::Wait; - -use crate::owned_object::OwnedObject; - -use crate::utils::{get_callback_global_ref, get_java_vm, load_on_close}; -use crate::zerror; -use std::ptr::null; - -use crate::throw_exception; - -trait SetJniSampleMissListenerCallback { - type WithCallback; - - unsafe fn set_jni_sample_miss_callback( - self, - env: &mut JNIEnv, - callback: JObject, - on_close: JObject, - ) -> ZResult; -} - -impl<'a> SetJniSampleMissListenerCallback for SampleMissListenerBuilder<'a, DefaultHandler> { - type WithCallback = SampleMissListenerBuilder<'a, Callback>; - - unsafe fn set_jni_sample_miss_callback( - self, - env: &mut JNIEnv, - callback: JObject, - on_close: JObject, - ) -> ZResult { - let java_vm = Arc::new(get_java_vm(env)?); - let callback_global_ref = get_callback_global_ref(env, callback)?; - let on_close_global_ref = get_callback_global_ref(env, on_close)?; - let on_close = load_on_close(&java_vm, on_close_global_ref); - - let builder = self.callback(move |miss| { - on_close.noop(); // Moves `on_close` inside the closure so it gets destroyed with the closure - let _ = || -> ZResult<()> { - let mut env = java_vm.attach_current_thread_as_daemon().map_err(|err| { - zerror!("Unable to attach thread for sample miss listener: {}", err) - })?; - - let (zid_lower, zid_upper, eid) = { - let id = miss.source(); - - let zid = id.zid().to_le_bytes(); - let zid_lower = i64::from_le_bytes(zid[0..8].try_into().unwrap()); - let zid_upper = i64::from_le_bytes(zid[8..16].try_into().unwrap()); - - (zid_lower, zid_upper, id.eid()) - }; - let missed_count = miss.nb(); - - env.call_method( - &callback_global_ref, - "run", - "(JJJJ)V", - &[ - JValue::from(zid_lower), - JValue::from(zid_upper), - JValue::from(eid as i64), - JValue::from(missed_count as i64), - ], - ) - .map_err(|err| zerror!(err))?; - Ok(()) - }() - .map_err(|err| tracing::error!("On sample miss listener callback error: {err}")); - }); - Ok(builder) - } -} - -/// Declares a subscriber to detect matching publishers for an [AdvancedSubscriber] via JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `advanced_subscriber_ptr`: The raw pointer to the [AdvancedSubscriber]. -/// - `callback`: The callback function as an instance of the `JNISubscriberCallback` interface in Java/Kotlin. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called upon closing the subscriber. -/// -/// Returns: -/// - A raw pointer to the declared [Subscriber]. In case of failure, an exception is thrown and null is returned. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided [AdvancedSubscriber] pointer is valid and has not been modified or freed. -/// - The [AdvancedSubscriber] pointer remains valid and the ownership of the [AdvancedSubscriber] is not transferred, -/// allowing safe usage of the [AdvancedSubscriber] after this function call. -/// - The callback function passed as `callback` must be a valid instance of the `JNISubscriberCallback` interface -/// in Java/Kotlin, matching the specified signature. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -#[cfg(feature = "zenoh-ext")] -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIAdvancedSubscriber_declareDetectPublishersSubscriberViaJNI( - mut env: JNIEnv, - _class: JClass, - advanced_subscriber_ptr: *const AdvancedSubscriber<()>, - history: jboolean, - callback: JObject, - on_close: JObject, -) -> *const Subscriber<()> { - let advanced_subscriber = OwnedObject::from_raw(advanced_subscriber_ptr); - - || -> ZResult<*const Subscriber<()>> { - tracing::debug!( - "Declaring detect publishers subscriber on '{}'...", - advanced_subscriber.key_expr() - ); - - let detect_publishers_subscriber = advanced_subscriber - .detect_publishers() - .history(history != 0) - .set_jni_sample_callback(&mut env, callback, on_close)? - .wait() - .map_err(|err| zerror!("Unable to declare detect publishers subscriber: {}", err))?; - - tracing::debug!( - "Detect publishers subscriber declared on '{}'...", - advanced_subscriber.key_expr() - ); - Ok(Arc::into_raw(Arc::new(detect_publishers_subscriber))) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }) -} - -/// Declares a background subscriber to detect matching publishers for an [AdvancedSubscriber] via JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `advanced_subscriber_ptr`: The raw pointer to the [AdvancedSubscriber]. -/// - `callback`: The callback function as an instance of the `JNISubscriberCallback` interface in Java/Kotlin. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called upon closing the subscriber. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided [AdvancedSubscriber] pointer is valid and has not been modified or freed. -/// - The [AdvancedSubscriber] pointer remains valid and the ownership of the [AdvancedSubscriber] is not transferred, -/// allowing safe usage of the [AdvancedSubscriber] after this function call. -/// - The callback function passed as `callback` must be a valid instance of the `JNISubscriberCallback` interface -/// in Java/Kotlin, matching the specified signature. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -#[cfg(feature = "zenoh-ext")] -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIAdvancedSubscriber_declareBackgroundDetectPublishersSubscriberViaJNI( - mut env: JNIEnv, - _class: JClass, - advanced_subscriber_ptr: *const AdvancedSubscriber<()>, - history: jboolean, - callback: JObject, - on_close: JObject, -) { - let advanced_subscriber = OwnedObject::from_raw(advanced_subscriber_ptr); - - || -> ZResult<()> { - tracing::debug!( - "Declaring background detect publishers subscriber on '{}'...", - advanced_subscriber.key_expr() - ); - - advanced_subscriber - .detect_publishers() - .history(history != 0) - .set_jni_sample_callback(&mut env, callback, on_close)? - .background() - .wait() - .map_err(|err| { - zerror!( - "Unable to declare background detect publishers subscriber: {}", - err - ) - })?; - - tracing::debug!( - "Background detect publishers subscriber declared on '{}'...", - advanced_subscriber.key_expr() - ); - Ok(()) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - }); -} - -/// Declares a [SampleMissListener] to detect missed samples for an [AdvancedSubscriber] via JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `advanced_subscriber_ptr`: The raw pointer to the [AdvancedSubscriber]. -/// - `callback`: The callback function as an instance of the `JNISampleMissListenerCallback` interface in Java/Kotlin. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called upon closing the subscriber. -/// -/// Returns: -/// - A raw pointer to the declared [SampleMissListener]. In case of failure, an exception is thrown and null is returned. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided [AdvancedSubscriber] pointer is valid and has not been modified or freed. -/// - The [AdvancedSubscriber] pointer remains valid and the ownership of the [AdvancedSubscriber] is not transferred, -/// allowing safe usage of the [AdvancedSubscriber] after this function call. -/// - The callback function passed as `callback` must be a valid instance of the `JNISampleMissListenerCallback` interface -/// in Java/Kotlin, matching the specified signature. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -#[cfg(feature = "zenoh-ext")] -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIAdvancedSubscriber_declareSampleMissListenerViaJNI( - mut env: JNIEnv, - _class: JClass, - advanced_subscriber_ptr: *const AdvancedSubscriber<()>, - - callback: JObject, - on_close: JObject, -) -> *const SampleMissListener<()> { - let advanced_subscriber = OwnedObject::from_raw(advanced_subscriber_ptr); - - || -> ZResult<*const SampleMissListener<()>> { - tracing::debug!( - "Declaring sample miss listener on '{}'...", - advanced_subscriber.key_expr() - ); - - let result = advanced_subscriber - .sample_miss_listener() - .set_jni_sample_miss_callback(&mut env, callback, on_close)? - .wait(); - - let sample_miss_listener = - result.map_err(|err| zerror!("Unable to declare sample miss listener: {}", err))?; - - tracing::debug!( - "Matching listener declared on '{}'...", - advanced_subscriber.key_expr() - ); - Ok(Arc::into_raw(Arc::new(sample_miss_listener))) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }) -} - -/// Declare a background sample miss listener for [AdvancedSubscriber] via JNI. -/// Register the listener callback to be run in background until the [AdvancedSubscriber] is undeclared. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `advanced_subscriber_ptr`: The raw pointer to an [AdvancedSubscriber]. -/// - `callback`: The callback function as an instance of the `JNISampleMissListenerCallback` interface in Java/Kotlin. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called upon undeclaring the [AdvancedSubscriber]. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided [AdvancedSubscriber] pointer is valid and has not been modified or freed. -/// - The [AdvancedSubscriber] pointer remains valid and the ownership of the [AdvancedSubscriber] is not transferred, -/// allowing safe usage of the [AdvancedSubscriber] after this function call. -/// - The callback function passed as `callback` must be a valid instance of the `JNISampleMissListenerCallback` interface -/// in Java/Kotlin, matching the specified signature. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -#[cfg(feature = "zenoh-ext")] -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIAdvancedSubscriber_declareBackgroundSampleMissListenerViaJNI( - mut env: JNIEnv, - _class: JClass, - advanced_subscriber_ptr: *const AdvancedSubscriber<()>, - - callback: JObject, - on_close: JObject, -) { - let advanced_subscriber = OwnedObject::from_raw(advanced_subscriber_ptr); - - || -> ZResult<()> { - tracing::debug!( - "Declaring background sample miss listener on '{}'...", - advanced_subscriber.key_expr() - ); - - advanced_subscriber - .sample_miss_listener() - .set_jni_sample_miss_callback(&mut env, callback, on_close)? - .background() - .wait() - .map_err(|err| zerror!("Unable to declare background sample miss listener: {}", err))?; - - tracing::debug!( - "Background sample miss listener declared on '{}'...", - advanced_subscriber.key_expr() - ); - Ok(()) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - }) -} - -/// Frees the [AdvancedSubscriber]. -/// -/// # Parameters: -/// - `_env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `subscriber_ptr`: The raw pointer to the [AdvancedSubscriber]. -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation. -/// - It assumes that the provided [AdvancedSubscriber] pointer is valid and has not been modified or freed. -/// - The function takes ownership of the raw pointer and releases the associated memory. -/// - After calling this function, the [AdvancedSubscriber] pointer becomes invalid and should not be used anymore. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIAdvancedSubscriber_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - subscriber_ptr: *const AdvancedSubscriber<()>, -) { - Arc::from_raw(subscriber_ptr); -} diff --git a/zenoh-jni/src/ext/matching_listener.rs b/zenoh-jni/src/ext/matching_listener.rs deleted file mode 100644 index fd8856e1..00000000 --- a/zenoh-jni/src/ext/matching_listener.rs +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (c) 2025 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use jni::{objects::JClass, JNIEnv}; -use zenoh::matching::MatchingListener; - -/// Frees the [MatchingListener]. -/// -/// # Parameters: -/// - `_env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `matching_listener_ptr`: The raw pointer to the [MatchingListener]. -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation. -/// - It assumes that the provided [MatchingListener] pointer is valid and has not been modified or freed. -/// - The function takes ownership of the raw pointer and releases the associated memory. -/// - After calling this function, the [MatchingListener] pointer becomes invalid and should not be used anymore. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIMatchingListener_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - matching_listener_ptr: *const MatchingListener<()>, -) { - Arc::from_raw(matching_listener_ptr); -} diff --git a/zenoh-jni/src/ext/mod.rs b/zenoh-jni/src/ext/mod.rs deleted file mode 100644 index 193fd21c..00000000 --- a/zenoh-jni/src/ext/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright (c) 2025 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -mod advanced_publisher; -mod advanced_subscriber; -mod matching_listener; -mod sample_miss_listener; diff --git a/zenoh-jni/src/ext/sample_miss_listener.rs b/zenoh-jni/src/ext/sample_miss_listener.rs deleted file mode 100644 index ede727b6..00000000 --- a/zenoh-jni/src/ext/sample_miss_listener.rs +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (c) 2025 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use jni::{objects::JClass, JNIEnv}; -use zenoh_ext::SampleMissListener; - -/// Frees the [SampleMissListener]. -/// -/// # Parameters: -/// - `_env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `sample_miss_listener_ptr`: The raw pointer to the [SampleMissListener]. -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation. -/// - It assumes that the provided [SampleMissListener] pointer is valid and has not been modified or freed. -/// - The function takes ownership of the raw pointer and releases the associated memory. -/// - After calling this function, the [SampleMissListener] pointer becomes invalid and should not be used anymore. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNISampleMissListener_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - sample_miss_listener_ptr: *const SampleMissListener<()>, -) { - Arc::from_raw(sample_miss_listener_ptr); -} diff --git a/zenoh-jni/src/key_expr.rs b/zenoh-jni/src/key_expr.rs deleted file mode 100644 index 11d39d47..00000000 --- a/zenoh-jni/src/key_expr.rs +++ /dev/null @@ -1,333 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::ops::Deref; -use std::sync::Arc; - -use jni::objects::JClass; -use jni::sys::{jboolean, jint, jstring}; -use jni::{objects::JString, JNIEnv}; -use zenoh::key_expr::KeyExpr; - -use crate::errors::ZResult; -use crate::utils::decode_string; -use crate::{throw_exception, zerror}; - -/// Validates the provided `key_expr` to be a valid key expression, returning it back -/// in case of success or throwing an exception in case of failure. -/// -/// # Parameters: -/// `env`: The JNI environment. -/// `_class`: the Java class (unused). -/// `key_expr`: Java string representation of the intended key expression. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_tryFromViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr: JString, -) -> jstring { - validate_key_expr(&mut env, &key_expr) - .map(|_| **key_expr) - .unwrap_or_else(|err| { - throw_exception!(env, err); - JString::default().as_raw() - }) -} - -/// Returns a java string representation of the autocanonized version of the provided `key_expr`. -/// In case of failure and exception will be thrown. -/// -/// # Parameters: -/// `env`: The JNI environment. -/// `_class`: the Java class (unused). -/// `key_expr`: Java string representation of the intended key expression. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_autocanonizeViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr: JString, -) -> jstring { - autocanonize_key_expr(&mut env, &key_expr) - .and_then(|key_expr| { - env.new_string(key_expr.to_string()) - .map(|kexp| kexp.as_raw()) - .map_err(|err| zerror!(err)) - }) - .unwrap_or_else(|err| { - throw_exception!(env, err); - JString::default().as_raw() - }) -} - -/// Returns true in case key_expr_1 intersects key_expr_2. -/// -/// # Params: -/// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. -/// - `key_expr_str_1`: String representation of the key expression 1. -/// - `key_expr_ptr_2`: Pointer to the key expression 2, differs from null only if it's a declared key expr. -/// - `key_expr_str_2`: String representation of the key expression 2. -/// -/// # Safety -/// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing -/// key expressions that were declared from a session (in that case the key expression has a pointer associated). -/// -/// In that case, this function assumes the pointers are valid pointers to key expressions and those pointers -/// remain valid after the call to this function. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_intersectsViaJNI( - mut env: JNIEnv, - _: JClass, - key_expr_ptr_1: /*nullable*/ *const KeyExpr<'static>, - key_expr_str_1: JString, - key_expr_ptr_2: /*nullable*/ *const KeyExpr<'static>, - key_expr_str_2: JString, -) -> jboolean { - || -> ZResult { - let key_expr_1 = process_kotlin_key_expr(&mut env, &key_expr_str_1, key_expr_ptr_1)?; - let key_expr_2 = process_kotlin_key_expr(&mut env, &key_expr_str_2, key_expr_ptr_2)?; - Ok(key_expr_1.intersects(&key_expr_2) as jboolean) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - false as jboolean - }) -} - -/// Returns true in case key_expr_1 includes key_expr_2. -/// -/// # Params: -/// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. -/// - `key_expr_str_1`: String representation of the key expression 1. -/// - `key_expr_ptr_2`: Pointer to the key expression 2, differs from null only if it's a declared key expr. -/// - `key_expr_str_2`: String representation of the key expression 2. -/// -/// # Safety -/// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing -/// key expressions that were declared from a session (in that case the key expression has a pointer associated). -/// -/// In that case, this function assumes the pointers are valid pointers to key expressions and those pointers -/// remain valid after the call to this function. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_includesViaJNI( - mut env: JNIEnv, - _: JClass, - key_expr_ptr_1: /*nullable*/ *const KeyExpr<'static>, - key_expr_str_1: JString, - key_expr_ptr_2: /*nullable*/ *const KeyExpr<'static>, - key_expr_str_2: JString, -) -> jboolean { - || -> ZResult { - let key_expr_1 = process_kotlin_key_expr(&mut env, &key_expr_str_1, key_expr_ptr_1)?; - let key_expr_2 = process_kotlin_key_expr(&mut env, &key_expr_str_2, key_expr_ptr_2)?; - Ok(key_expr_1.includes(&key_expr_2) as jboolean) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - false as jboolean - }) -} - -/// Returns the integer representation of the intersection level of the key expression 1 and key expression 2, -/// from the perspective of key expression 1. -/// -/// # Params: -/// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. -/// - `key_expr_str_1`: String representation of the key expression 1. -/// - `key_expr_ptr_2`: Pointer to the key expression 2, differs from null only if it's a declared key expr. -/// - `key_expr_str_2`: String representation of the key expression 2. -/// -/// # Safety -/// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing -/// key expressions that were declared from a session (in that case the key expression has a pointer associated). -/// -/// In that case, this function assumes the pointers are valid pointers to key expressions and those pointers -/// remain valid after the call to this function. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_relationToViaJNI( - mut env: JNIEnv, - _: JClass, - key_expr_ptr_1: /*nullable*/ *const KeyExpr<'static>, - key_expr_str_1: JString, - key_expr_ptr_2: /*nullable*/ *const KeyExpr<'static>, - key_expr_str_2: JString, -) -> jint { - || -> ZResult { - let key_expr_1 = process_kotlin_key_expr(&mut env, &key_expr_str_1, key_expr_ptr_1)?; - let key_expr_2 = process_kotlin_key_expr(&mut env, &key_expr_str_2, key_expr_ptr_2)?; - Ok(key_expr_1.relation_to(&key_expr_2) as jint) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - -1 as jint - }) -} - -/// Joins key expression 1 with key expression 2, where key_expr_2 is a string. Returns the string representation -/// of the result, or throws an exception in case of failure. -/// -/// # Params: -/// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. -/// - `key_expr_ptr_1`: String representation of the key expression 1. -/// - `key_expr_2`: String representation of the key expression 2. -/// -/// # Safety -/// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing -/// key expressions that were declared from a session (in that case the key expression has a pointer associated). -/// -/// In that case, this function assumes the pointers are valid pointers to key expressions and those pointers -/// remain valid after the call to this function. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_joinViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr_ptr_1: /*nullable*/ *const KeyExpr<'static>, - key_expr_str_1: JString, - key_expr_2: JString, -) -> jstring { - || -> ZResult { - let key_expr_1 = process_kotlin_key_expr(&mut env, &key_expr_str_1, key_expr_ptr_1)?; - let key_expr_2_str = decode_string(&mut env, &key_expr_2)?; - let result = key_expr_1 - .join(key_expr_2_str.as_str()) - .map_err(|err| zerror!(err))?; - env.new_string(result.to_string()) - .map(|kexp| kexp.as_raw()) - .map_err(|err| zerror!(err)) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - JString::default().as_raw() - }) -} - -/// Concats key_expr_1 with key_expr_2, where key_expr_2 is a string. Returns the string representation -/// of the result, or throws an exception in case of failure. -/// -/// # Params: -/// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. -/// - `key_expr_ptr_1`: String representation of the key expression 1. -/// - `key_expr_2`: String representation of the key expression 2. -/// -/// # Safety -/// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing -/// key expressions that were declared from a session (in that case the key expression has a pointer associated). -/// -/// In that case, this function assumes the pointers are valid pointers to key expressions and those pointers -/// remain valid after the call to this function. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_concatViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr_ptr_1: /*nullable*/ *const KeyExpr<'static>, - key_expr_str_1: JString, - key_expr_2: JString, -) -> jstring { - || -> ZResult { - let key_expr_1 = process_kotlin_key_expr(&mut env, &key_expr_str_1, key_expr_ptr_1)?; - let key_expr_2_str = decode_string(&mut env, &key_expr_2)?; - let result = key_expr_1 - .concat(key_expr_2_str.as_str()) - .map_err(|err| zerror!(err))?; - env.new_string(result.to_string()) - .map(|kexp| kexp.as_raw()) - .map_err(|err| zerror!(err)) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - JString::default().as_raw() - }) -} - -/// Frees a declared key expression. -/// -/// # Parameters -/// - `_env`: Unused. The JNI environment. -/// - `_class`: Unused. The java class from which the function was called. -/// - `key_expr_ptr`: the pointer to the key expression. -/// -/// # Safety -/// - This function assumes the provided pointer is valid and points to a native key expression. -/// - The memory associated to the pointer is freed after returning from this call, turning the -/// pointer invalid after that. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - key_expr_ptr: *const KeyExpr<'static>, -) { - Arc::from_raw(key_expr_ptr); -} - -fn validate_key_expr(env: &mut JNIEnv, key_expr: &JString) -> ZResult> { - let key_expr_str = decode_string(env, key_expr) - .map_err(|err| zerror!("Unable to get key expression string value: '{}'.", err))?; - - KeyExpr::try_from(key_expr_str) - .map_err(|err| zerror!("Unable to create key expression: '{}'.", err)) -} - -fn autocanonize_key_expr(env: &mut JNIEnv, key_expr: &JString) -> ZResult> { - decode_string(env, key_expr) - .map_err(|err| zerror!("Unable to get key expression string value: '{}'.", err)) - .and_then(|key_expr_str| { - KeyExpr::autocanonize(key_expr_str) - .map_err(|err| zerror!("Unable to create key expression: '{}'", err)) - }) -} - -/// Processes a kotlin key expression. -/// -/// Receives the Java/Kotlin string representation of the key expression as well as a pointer. -/// The pointer is only valid in cases where the key expression is associated to a native pointer -/// (when the key expression was declared from a session). -/// If the pointer is valid, the key expression returned is the key expression the pointer pointed to. -/// Otherwise, a key expression created from the string representation of the key expression is returned. -/// -/// # Safety: -/// -/// The key_expr_str argument provided should already have been validated upon creation of the -/// KeyExpr instance on Kotlin. -/// -pub(crate) unsafe fn process_kotlin_key_expr( - env: &mut JNIEnv, - key_expr_str: &JString, - key_expr_ptr: *const KeyExpr<'static>, -) -> ZResult> { - if key_expr_ptr.is_null() { - let key_expr = decode_string(env, key_expr_str) - .map_err(|err| zerror!("Unable to get key expression string value: '{}'.", err))?; - Ok(KeyExpr::from_string_unchecked(key_expr)) - } else { - let key_expr = Arc::from_raw(key_expr_ptr); - let key_expr_clone = key_expr.deref().clone(); - std::mem::forget(key_expr); - Ok(key_expr_clone) - } -} diff --git a/zenoh-jni/src/lib.rs b/zenoh-jni/src/lib.rs deleted file mode 100644 index 93765edb..00000000 --- a/zenoh-jni/src/lib.rs +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -mod config; -mod errors; -#[cfg(feature = "zenoh-ext")] -mod ext; -mod key_expr; -mod liveliness; -mod logger; -pub(crate) mod owned_object; -mod publisher; -mod querier; -mod query; -mod queryable; -pub(crate) mod sample_callback; -mod scouting; -mod session; -mod subscriber; -mod utils; -#[cfg(feature = "zenoh-ext")] -mod zbytes; -mod zenoh_id; - -// Test should be runned with `cargo test --no-default-features` -#[test] -#[cfg(not(feature = "default"))] -fn test_no_default_features() { - assert_eq!(zenoh::FEATURES, concat!(" zenoh/unstable")); -} diff --git a/zenoh-jni/src/liveliness.rs b/zenoh-jni/src/liveliness.rs deleted file mode 100644 index d822eb94..00000000 --- a/zenoh-jni/src/liveliness.rs +++ /dev/null @@ -1,174 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::{ptr::null, sync::Arc, time::Duration}; - -use jni::{ - objects::{JClass, JObject, JString}, - sys::{jboolean, jlong}, - JNIEnv, -}; - -use zenoh::{ - internal::runtime::ZRuntime, key_expr::KeyExpr, liveliness::LivelinessToken, - pubsub::Subscriber, Session, Wait, -}; - -use crate::{ - errors::ZResult, - key_expr::process_kotlin_key_expr, - owned_object::OwnedObject, - sample_callback::SetJniSampleCallback, - session::{on_reply_error, on_reply_success}, - throw_exception, - utils::{get_callback_global_ref, get_java_vm, load_on_close}, - zerror, -}; - -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNILiveliness_getViaJNI( - mut env: JNIEnv, - _class: JClass, - session_ptr: *const Session, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - callback: JObject, - timeout_ms: jlong, - on_close: JObject, -) { - let session = unsafe { OwnedObject::from_raw(session_ptr) }; - let _ = || -> ZResult<()> { - let key_expr = unsafe { process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr) }?; - let java_vm = Arc::new(get_java_vm(&mut env)?); - let callback_global_ref = get_callback_global_ref(&mut env, callback)?; - let on_close_global_ref = get_callback_global_ref(&mut env, on_close)?; - let on_close = load_on_close(&java_vm, on_close_global_ref); - let timeout = Duration::from_millis(timeout_ms as u64); - let replies = session - .liveliness() - .get(key_expr.to_owned()) - .timeout(timeout) - .wait() - .map_err(|err| zerror!(err))?; - - ZRuntime::Application.spawn(async move { - on_close.noop(); // Does nothing, but moves `on_close` inside the closure so it gets destroyed with the closure - while let Ok(reply) = replies.recv_async().await { - || -> ZResult<()> { - tracing::debug!("Receiving liveliness reply through JNI: {:?}", reply); - let mut env = java_vm.attach_current_thread_as_daemon().map_err(|err| { - zerror!( - "Unable to attach thread for GET liveliness query callback: {}.", - err - ) - })?; - match reply.result() { - Ok(sample) => on_reply_success( - &mut env, - reply.replier_id(), - sample, - &callback_global_ref, - ), - Err(error) => on_reply_error( - &mut env, - reply.replier_id(), - error, - &callback_global_ref, - ), - } - }() - .unwrap_or_else(|err| tracing::error!("Error on get liveliness callback: {err}.")); - } - }); - Ok(()) - }() - .map_err(|err| { - throw_exception!(env, err); - }); -} - -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNILiveliness_declareTokenViaJNI( - mut env: JNIEnv, - _class: JClass, - session_ptr: *const Session, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, -) -> *const LivelinessToken { - let session = unsafe { OwnedObject::from_raw(session_ptr) }; - || -> ZResult<*const LivelinessToken> { - let key_expr = unsafe { process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr) }?; - tracing::trace!("Declaring liveliness token on '{key_expr}'."); - let token = session - .liveliness() - .declare_token(key_expr) - .wait() - .map_err(|err| zerror!(err))?; - Ok(Arc::into_raw(Arc::new(token))) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }) -} - -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNILivelinessToken_00024Companion_undeclareViaJNI( - _env: JNIEnv, - _: JClass, - token_ptr: *const LivelinessToken, -) { - unsafe { Arc::from_raw(token_ptr) }; -} - -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNILiveliness_declareSubscriberViaJNI( - mut env: JNIEnv, - _class: JClass, - session_ptr: *const Session, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - callback: JObject, - history: jboolean, - on_close: JObject, -) -> *const Subscriber<()> { - let session = unsafe { OwnedObject::from_raw(session_ptr) }; - || -> ZResult<*const Subscriber<()>> { - let key_expr = unsafe { process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)? }; - tracing::debug!("Declaring liveliness subscriber on '{}'...", key_expr); - - let result = unsafe { - session - .liveliness() - .declare_subscriber(key_expr.to_owned()) - .history(history != 0) - .set_jni_sample_callback(&mut env, callback, on_close)? - .wait() - }; - - let subscriber = - result.map_err(|err| zerror!("Unable to declare liveliness subscriber: {}", err))?; - - tracing::debug!("Liveliness subscriber declared on '{}'.", key_expr); - Ok(Arc::into_raw(Arc::new(subscriber))) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }) -} diff --git a/zenoh-jni/src/logger.rs b/zenoh-jni/src/logger.rs deleted file mode 100644 index 12047a3e..00000000 --- a/zenoh-jni/src/logger.rs +++ /dev/null @@ -1,79 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use jni::{ - objects::{JClass, JString}, - JNIEnv, -}; - -use crate::{errors::ZResult, throw_exception, zerror}; - -/// Redirects the Rust logs either to logcat for Android systems or to the standard output (for non-Android systems). -/// -/// This function is meant to be called from Java/Kotlin code through JNI. It takes a `filter` -/// indicating the desired log level. -/// If the logger was already initialized in a previous call, then it does nothing. -/// -/// See https://docs.rs/env_logger/latest/env_logger/index.html for accepted filter format. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `filter`: The logs filter. -/// -/// # Errors: -/// - If there is an error parsing the log level string, a `JNIException` is thrown on the JVM. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_Logger_00024Companion_startLogsViaJNI( - mut env: JNIEnv, - _class: JClass, - filter: JString, -) { - || -> ZResult<()> { - let log_level = parse_filter(&mut env, filter)?; - #[cfg(target_os = "android")] - { - android_logd_logger::builder() - .parse_filters(log_level.as_str()) - .tag_target_strip() - .prepend_module(true) - .pstore(false) - .try_init() - .ok(); - } - - #[cfg(not(target_os = "android"))] - { - android_logd_logger::builder() - .parse_filters(log_level.as_str()) - .tag_target_strip() - .prepend_module(true) - .try_init() - .ok(); - } - - Ok(()) - }() - .unwrap_or_else(|err| throw_exception!(env, err)) -} - -fn parse_filter(env: &mut JNIEnv, log_level: JString) -> ZResult { - let log_level = env.get_string(&log_level).map_err(|err| zerror!(err))?; - log_level - .to_str() - .map(|level| Ok(level.to_string())) - .map_err(|err| zerror!(err))? -} diff --git a/zenoh-jni/src/owned_object.rs b/zenoh-jni/src/owned_object.rs deleted file mode 100644 index 25f1c521..00000000 --- a/zenoh-jni/src/owned_object.rs +++ /dev/null @@ -1,46 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::{ops::Deref, sync::Arc}; - -/// Safe accessor to refocounted ([Arc]) owned objects. -/// Helps to avoid early drop by offloading [std::mem::forget] from user -pub(crate) struct OwnedObject { - inner: Option>, -} - -impl Deref for OwnedObject { - type Target = T; - - fn deref(&self) -> &Self::Target { - // SAFETY: inner is always initialized - unsafe { self.inner.as_ref().unwrap_unchecked() } - } -} - -impl Drop for OwnedObject { - fn drop(&mut self) { - // SAFETY: inner is always initialized - let inner = unsafe { self.inner.take().unwrap_unchecked() }; - std::mem::forget(inner); - } -} - -impl OwnedObject { - pub(crate) unsafe fn from_raw(ptr: *const T) -> Self { - Self { - inner: Some(Arc::from_raw(ptr)), - } - } -} diff --git a/zenoh-jni/src/publisher.rs b/zenoh-jni/src/publisher.rs deleted file mode 100644 index ead60c3f..00000000 --- a/zenoh-jni/src/publisher.rs +++ /dev/null @@ -1,130 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use jni::{ - objects::{JByteArray, JClass, JString}, - sys::jint, - JNIEnv, -}; -use zenoh::{pubsub::Publisher, Wait}; - -use crate::throw_exception; -use crate::{ - errors::ZResult, - utils::{decode_byte_array, decode_encoding}, - zerror, -}; - -/// Performs a PUT operation on a Zenoh publisher via JNI. -/// -/// # Parameters -/// - `env`: The JNI environment pointer. -/// - `_class`: The Java class reference (unused). -/// - `payload`: The byte array to be published. -/// - `encoding_id`: The encoding ID of the payload. -/// - `encoding_schema`: Nullable encoding schema string of the payload. -/// - `attachment`: Nullble byte array for the attachment. -/// - `publisher_ptr`: The raw pointer to the Zenoh publisher ([Publisher]). -/// -/// # Safety -/// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - Assumes that the provided publisher pointer is valid and has not been modified or freed. -/// - The publisher pointer remains valid after this function call. -/// - May throw an exception in case of failure, which must be handled by the caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_putViaJNI( - mut env: JNIEnv, - _class: JClass, - payload: JByteArray, - encoding_id: jint, - encoding_schema: /*nullable*/ JString, - attachment: /*nullable*/ JByteArray, - publisher_ptr: *const Publisher<'static>, -) { - let publisher = Arc::from_raw(publisher_ptr); - let _ = || -> ZResult<()> { - let payload = decode_byte_array(&env, payload)?; - let mut publication = publisher.put(payload); - let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; - publication = publication.encoding(encoding); - if !attachment.is_null() { - let attachment = decode_byte_array(&env, attachment)?; - publication = publication.attachment::>(attachment) - }; - publication.wait().map_err(|err| zerror!(err)) - }() - .map_err(|err| throw_exception!(env, err)); - std::mem::forget(publisher); -} - -/// Performs a DELETE operation on a Zenoh publisher via JNI. -/// -/// # Parameters -/// - `env`: The JNI environment pointer. -/// - `_class`: The Java class reference (unused). -/// - `attachment`: Nullble byte array for the attachment. -/// - `publisher_ptr`: The raw pointer to the Zenoh publisher ([Publisher]). -/// -/// # Safety -/// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - Assumes that the provided publisher pointer is valid and has not been modified or freed. -/// - The publisher pointer remains valid after this function call. -/// - May throw an exception in case of failure, which must be handled by the caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_deleteViaJNI( - mut env: JNIEnv, - _class: JClass, - attachment: /*nullable*/ JByteArray, - publisher_ptr: *const Publisher<'static>, -) { - let publisher = Arc::from_raw(publisher_ptr); - let _ = || -> ZResult<()> { - let mut delete = publisher.delete(); - if !attachment.is_null() { - let attachment = decode_byte_array(&env, attachment)?; - delete = delete.attachment::>(attachment) - }; - delete.wait().map_err(|err| zerror!(err)) - }() - .map_err(|err| throw_exception!(env, err)); - std::mem::forget(publisher) -} - -/// Frees the publisher. -/// -/// # Parameters: -/// - `_env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `publisher_ptr`: The raw pointer to the Zenoh publisher ([Publisher]). -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation. -/// - It assumes that the provided publisher pointer is valid and has not been modified or freed. -/// - After calling this function, the publisher pointer becomes invalid and should not be used anymore. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - publisher_ptr: *const Publisher, -) { - Arc::from_raw(publisher_ptr); -} diff --git a/zenoh-jni/src/querier.rs b/zenoh-jni/src/querier.rs deleted file mode 100644 index 8c239498..00000000 --- a/zenoh-jni/src/querier.rs +++ /dev/null @@ -1,137 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use jni::{ - objects::{JByteArray, JClass, JObject, JString}, - sys::jint, - JNIEnv, -}; -use zenoh::{key_expr::KeyExpr, query::Querier, Wait}; - -use crate::{ - errors::ZResult, - key_expr::process_kotlin_key_expr, - session::{on_reply_error, on_reply_success}, - throw_exception, - utils::{ - decode_byte_array, decode_encoding, decode_string, get_callback_global_ref, get_java_vm, - load_on_close, - }, - zerror, -}; - -/// Perform a Zenoh GET through a querier. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `querier_ptr`: The raw pointer to the querier. -/// - `key_expr_ptr`: A raw pointer to the [KeyExpr] provided to the kotlin querier. May be null in case of using an -/// undeclared key expression. -/// - `key_expr_str`: String representation of the key expression used during the querier declaration. -/// It won't be considered in case a key_expr_ptr to a declared key expression is provided. -/// - `selector_params`: Optional selector parameters for the query. -/// - `callback`: Reference to the Kotlin callback to be run upon receiving a reply. -/// - `on_close`: Reference to a kotlin callback to be run upon finishing the get operation, mostly used for closing a provided channel. -/// - `attachment`: Optional attachment. -/// - `payload`: Optional payload for the query. -/// - `encoding_id`: Encoding id of the payload provided. -/// - `encoding_schema`: Encoding schema of the payload provided. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIQuerier_getViaJNI( - mut env: JNIEnv, - _class: JClass, - querier_ptr: *const Querier, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - selector_params: /*nullable*/ JString, - callback: JObject, - on_close: JObject, - attachment: /*nullable*/ JByteArray, - payload: /*nullable*/ JByteArray, - encoding_id: jint, - encoding_schema: /*nullable*/ JString, -) { - let querier = Arc::from_raw(querier_ptr); - let _ = || -> ZResult<()> { - let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; - let java_vm = Arc::new(get_java_vm(&mut env)?); - let callback_global_ref = get_callback_global_ref(&mut env, callback)?; - let on_close_global_ref = get_callback_global_ref(&mut env, on_close)?; - let on_close = load_on_close(&java_vm, on_close_global_ref); - let mut get_builder = querier.get().callback(move |reply| { - || -> ZResult<()> { - on_close.noop(); // Does nothing, but moves `on_close` inside the closure so it gets destroyed with the closure - tracing::debug!("Receiving reply through JNI: {:?}", reply); - let mut env = java_vm.attach_current_thread_as_daemon().map_err(|err| { - zerror!("Unable to attach thread for GET query callback: {}.", err) - })?; - - match reply.result() { - Ok(sample) => { - on_reply_success(&mut env, reply.replier_id(), sample, &callback_global_ref) - } - Err(error) => { - on_reply_error(&mut env, reply.replier_id(), error, &callback_global_ref) - } - } - }() - .unwrap_or_else(|err| tracing::error!("Error on get callback: {err}")); - }); - - if !selector_params.is_null() { - let params = decode_string(&mut env, &selector_params)?; - get_builder = get_builder.parameters(params) - }; - - if !payload.is_null() { - let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; - get_builder = get_builder.encoding(encoding); - get_builder = get_builder.payload(decode_byte_array(&env, payload)?); - } - - if !attachment.is_null() { - let attachment = decode_byte_array(&env, attachment)?; - get_builder = get_builder.attachment::>(attachment); - } - - get_builder - .wait() - .map(|_| tracing::trace!("Performing get on '{key_expr}'.",)) - .map_err(|err| zerror!(err)) - }() - .map_err(|err| throw_exception!(env, err)); - std::mem::forget(querier); -} - -/// -/// Frees the pointer of the querier. -/// -/// After a call to this function, no further jni operations should be performed using the querier associated to the raw pointer provided. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQuerier_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - querier_ptr: *const Querier<'static>, -) { - Arc::from_raw(querier_ptr); -} diff --git a/zenoh-jni/src/query.rs b/zenoh-jni/src/query.rs deleted file mode 100644 index 95affb7e..00000000 --- a/zenoh-jni/src/query.rs +++ /dev/null @@ -1,206 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use crate::utils::{decode_byte_array, decode_encoding}; -use crate::zerror; -use crate::{errors::ZResult, key_expr::process_kotlin_key_expr, throw_exception}; -use jni::{ - objects::{JByteArray, JClass, JString}, - sys::{jboolean, jint, jlong}, - JNIEnv, -}; -use uhlc::ID; -use zenoh::{ - key_expr::KeyExpr, - query::Query, - time::{Timestamp, NTP64}, - Wait, -}; - -/// Replies with `success` to a Zenoh [Query] via JNI, freeing the query in the process. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `query_ptr`: The raw pointer to the Zenoh query. -/// - `key_expr_ptr`: Nullable key expression pointer associated with the query result. This parameter -/// is meant to be used with declared key expressions, which have a pointer associated to them. -/// In case of it being null, then the `key_expr_string` will be used to perform the reply. -/// - `key_expr_str`: The string representation of the key expression associated with the query result. -/// - `payload`: The payload for the reply. -/// - `encoding_id`: The encoding id of the payload. -/// - `encoding_schema`: Nullable encoding schema. -/// - `timestamp_enabled`: A boolean indicating whether the timestamp is enabled. -/// - `timestamp_ntp_64`: The NTP64 timestamp value. -/// - `attachment`: Nullable user attachment encoded as a byte array. -/// - `qos_express`: Whether the reply should be sent with the express flag. -/// -/// # Safety: -/// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided raw pointer to the Zenoh query is valid and has not been modified or freed. -/// - The query pointer is freed after calling this function (queries shouldn't be replied more than once), -/// therefore the query isn't valid anymore after that. -/// - May throw a JNI exception in case of failure, which should be handled by the caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQuery_replySuccessViaJNI( - mut env: JNIEnv, - _class: JClass, - query_ptr: *const Query, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - payload: JByteArray, - encoding_id: jint, - encoding_schema: /*nullable*/ JString, - timestamp_enabled: jboolean, - timestamp_ntp_64: jlong, - attachment: /*nullable*/ JByteArray, - qos_express: jboolean, -) { - let _ = || -> ZResult<()> { - let query = Arc::from_raw(query_ptr); - let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; - let payload = decode_byte_array(&env, payload)?; - let mut reply_builder = query.reply(key_expr, payload); - let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; - reply_builder = reply_builder.encoding(encoding); - if timestamp_enabled != 0 { - let ts = Timestamp::new(NTP64(timestamp_ntp_64 as u64), ID::rand()); - reply_builder = reply_builder.timestamp(ts) - } - if !attachment.is_null() { - reply_builder = reply_builder.attachment(decode_byte_array(&env, attachment)?); - } - reply_builder = reply_builder.express(qos_express != 0); - reply_builder.wait().map_err(|err| zerror!(err)) - }() - .map_err(|err| throw_exception!(env, err)); -} - -/// Replies with `error` to a Zenoh [Query] via JNI, freeing the query in the process. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `query_ptr`: The raw pointer to the Zenoh query. -/// - `payload`: The payload for the reply. -/// - `encoding_id`: The encoding id of the payload. -/// - `encoding_schema`: Nullable encoding schema. -/// -/// # Safety: -/// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided raw pointer to the Zenoh query is valid and has not been modified or freed. -/// - May throw a JNI exception in case of failure, which should be handled by the caller. -/// - The query pointer is freed after calling this function (queries shouldn't be replied more than once), -/// therefore the query isn't valid anymore after that. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQuery_replyErrorViaJNI( - mut env: JNIEnv, - _class: JClass, - query_ptr: *const Query, - payload: JByteArray, - encoding_id: jint, - encoding_schema: /*nullable*/ JString, -) { - let _ = || -> ZResult<()> { - let query = Arc::from_raw(query_ptr); - let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; - query - .reply_err(decode_byte_array(&env, payload)?) - .encoding(encoding) - .wait() - .map_err(|err| zerror!(err)) - }() - .map_err(|err| throw_exception!(env, err)); -} - -/// Replies with `delete` to a Zenoh [Query] via JNI, freeing the query in the process. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `query_ptr`: The raw pointer to the Zenoh query. -/// - `key_expr_ptr`: Nullable key expression pointer associated with the query result. This parameter -/// is meant to be used with declared key expressions, which have a pointer associated to them. -/// In case of it being null, then the `key_expr_string` will be used to perform the reply. -/// - `key_expr_str`: The string representation of the key expression associated with the query result. -/// - `timestamp_enabled`: A boolean indicating whether the timestamp is enabled. -/// - `timestamp_ntp_64`: The NTP64 timestamp value. -/// - `attachment`: Nullable user attachment encoded as a byte array. -/// - `qos_express`: Whether the reply should be sent with the express flag. -/// -/// # Safety: -/// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided raw pointer to the Zenoh query is valid and has not been modified or freed. -/// - May throw a JNI exception in case of failure, which should be handled by the caller. -/// - The query pointer is freed after calling this function (queries shouldn't be replied more than once), -/// therefore the query isn't valid anymore after that. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQuery_replyDeleteViaJNI( - mut env: JNIEnv, - _class: JClass, - query_ptr: *const Query, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - timestamp_enabled: jboolean, - timestamp_ntp_64: jlong, - attachment: /*nullable*/ JByteArray, - qos_express: jboolean, -) { - let _ = || -> ZResult<()> { - let query = Arc::from_raw(query_ptr); - let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; - let mut reply_builder = query.reply_del(key_expr); - if timestamp_enabled != 0 { - let ts = Timestamp::new(NTP64(timestamp_ntp_64 as u64), ID::rand()); - reply_builder = reply_builder.timestamp(ts) - } - if !attachment.is_null() { - reply_builder = reply_builder.attachment(decode_byte_array(&env, attachment)?); - } - reply_builder = reply_builder.express(qos_express != 0); - reply_builder.wait().map_err(|err| zerror!(err)) - }() - .map_err(|err| throw_exception!(env, err)); -} - -/// Frees the Query via JNI. -/// -/// Parameters: -/// - `_env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `ptr`: The raw pointer to the Zenoh query ([Query]). -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation. -/// - It assumes that the provided query pointer is valid and has not been modified or freed. -/// - The function takes ownership of the raw pointer and releases the associated memory. -/// - After calling this function, the query pointer becomes invalid and should not be used anymore. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQuery_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - query_ptr: *const Query, -) { - Arc::from_raw(query_ptr); -} diff --git a/zenoh-jni/src/queryable.rs b/zenoh-jni/src/queryable.rs deleted file mode 100644 index 5d2ddb1d..00000000 --- a/zenoh-jni/src/queryable.rs +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use jni::{objects::JClass, JNIEnv}; -use zenoh::query::Queryable; - -/// Frees the [Queryable]. -/// -/// # Parameters: -/// - `_env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `queryable_ptr`: The raw pointer to the Zenoh queryable ([Queryable]). -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation. -/// - It assumes that the provided queryable pointer is valid and has not been modified or freed. -/// - The function takes ownership of the raw pointer and releases the associated memory. -/// - After calling this function, the queryable pointer becomes invalid and should not be used anymore. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQueryable_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - queryable_ptr: *const Queryable<()>, -) { - Arc::from_raw(queryable_ptr); -} diff --git a/zenoh-jni/src/sample_callback.rs b/zenoh-jni/src/sample_callback.rs deleted file mode 100644 index 09181490..00000000 --- a/zenoh-jni/src/sample_callback.rs +++ /dev/null @@ -1,138 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use jni::{ - objects::{JByteArray, JObject, JString, JValue}, - sys::jint, - JNIEnv, -}; -use zenoh::{ - handlers::{Callback, DefaultHandler}, - liveliness::LivelinessSubscriberBuilder, - pubsub::SubscriberBuilder, - sample::Sample, -}; - -use crate::{errors::ZResult, utils::*, zerror}; - -pub(crate) trait SetJniSampleCallback: Sized + HasSampleCallbackSetter { - unsafe fn set_jni_sample_callback( - self, - env: &mut JNIEnv, - callback: JObject, - on_close: JObject, - ) -> ZResult { - let java_vm = Arc::new(get_java_vm(env)?); - let callback_global_ref = get_callback_global_ref(env, callback)?; - let on_close_global_ref = get_callback_global_ref(env, on_close)?; - let on_close = load_on_close(&java_vm, on_close_global_ref); - - let builder = self.set_callback(move |sample: Sample| { - on_close.noop(); // Moves `on_close` inside the closure so it gets destroyed with the closure - let _ = || -> ZResult<()> { - let mut env = java_vm.attach_current_thread_as_daemon().map_err(|err| { - zerror!("Unable to attach thread for sample callback: {}", err) - })?; - let byte_array = bytes_to_java_array(&env, sample.payload()) - .map(|array| env.auto_local(array))?; - - let encoding_id: jint = sample.encoding().id() as jint; - let encoding_schema = match sample.encoding().schema() { - Some(schema) => slice_to_java_string(&env, schema)?, - None => JString::default(), - }; - let kind = sample.kind() as jint; - let (timestamp, is_valid) = sample - .timestamp() - .map(|timestamp| (timestamp.get_time().as_u64(), true)) - .unwrap_or((0, false)); - - let attachment_bytes = sample - .attachment() - .map_or_else( - || Ok(JByteArray::default()), - |attachment| bytes_to_java_array(&env, attachment), - ) - .map(|array| env.auto_local(array)) - .map_err(|err| zerror!("Error processing attachment: {}", err))?; - - let key_expr_str = env.auto_local( - env.new_string(sample.key_expr().to_string()) - .map_err(|err| zerror!("Error processing sample key expr: {}", err))?, - ); - - let express = sample.express(); - let priority = sample.priority() as jint; - let cc = sample.congestion_control() as jint; - - env.call_method( - &callback_global_ref, - "run", - "(Ljava/lang/String;[BILjava/lang/String;IJZ[BZII)V", - &[ - JValue::from(&key_expr_str), - JValue::from(&byte_array), - JValue::from(encoding_id), - JValue::from(&encoding_schema), - JValue::from(kind), - JValue::from(timestamp as i64), - JValue::from(is_valid), - JValue::from(&attachment_bytes), - JValue::from(express), - JValue::from(priority), - JValue::from(cc), - ], - ) - .map_err(|err| zerror!(err))?; - Ok(()) - }() - .map_err(|err| tracing::error!("On sample callback error: {err}")); - }); - Ok(builder) - } -} - -impl SetJniSampleCallback for T {} - -pub(crate) trait HasSampleCallbackSetter { - type BuilderWithCallback; - - fn set_callback(self, callback: F) -> Self::BuilderWithCallback - where - F: Fn(Sample) + Send + Sync + 'static; -} - -impl<'a, 'b> HasSampleCallbackSetter for SubscriberBuilder<'a, 'b, DefaultHandler> { - type BuilderWithCallback = SubscriberBuilder<'a, 'b, Callback>; - - fn set_callback(self, callback: F) -> Self::BuilderWithCallback - where - F: Fn(Sample) + Send + Sync + 'static, - { - self.callback(callback) - } -} - -impl<'a, 'b> HasSampleCallbackSetter for LivelinessSubscriberBuilder<'a, 'b, DefaultHandler> { - type BuilderWithCallback = LivelinessSubscriberBuilder<'a, 'b, Callback>; - - fn set_callback(self, callback: F) -> Self::BuilderWithCallback - where - F: Fn(Sample) + Send + Sync + 'static, - { - self.callback(callback) - } -} diff --git a/zenoh-jni/src/scouting.rs b/zenoh-jni/src/scouting.rs deleted file mode 100644 index b0a665c1..00000000 --- a/zenoh-jni/src/scouting.rs +++ /dev/null @@ -1,113 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::{ptr::null, sync::Arc}; - -use jni::{ - objects::{GlobalRef, JClass, JList, JObject, JValue}, - sys::jint, - JNIEnv, -}; -use zenoh::{config::WhatAmIMatcher, Wait}; -use zenoh::{scouting::Scout, Config}; - -use crate::utils::{get_callback_global_ref, get_java_vm, load_on_close}; -use crate::{errors::ZResult, throw_exception, zerror}; - -/// Start a scout. -/// -/// # Params -/// - `whatAmI`: Ordinal value of the WhatAmI enum. -/// - `callback`: Callback to be executed whenever a hello message is received. -/// - `config_ptr`: Optional config pointer. -/// -/// Returns a pointer to the scout, which must be freed afterwards. -/// If starting the scout fails, an exception is thrown on the JVM, and a null pointer is returned. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIScout_00024Companion_scoutViaJNI( - mut env: JNIEnv, - _class: JClass, - whatAmI: jint, - callback: JObject, - on_close: JObject, - config_ptr: /*nullable=*/ *const Config, -) -> *const Scout<()> { - || -> ZResult<*const Scout<()>> { - let callback_global_ref = get_callback_global_ref(&mut env, callback)?; - let java_vm = Arc::new(get_java_vm(&mut env)?); - let on_close_global_ref: GlobalRef = get_callback_global_ref(&mut env, on_close)?; - let on_close = load_on_close(&java_vm, on_close_global_ref); - let whatAmIMatcher: WhatAmIMatcher = (whatAmI as u8).try_into().unwrap(); // The validity of the operation is guaranteed on the kotlin layer. - let config = if config_ptr.is_null() { - Config::default() - } else { - let arc_cfg = Arc::from_raw(config_ptr); - let config_clone = arc_cfg.as_ref().clone(); - std::mem::forget(arc_cfg); - config_clone - }; - zenoh::scout(whatAmIMatcher, config) - .callback(move |hello| { - on_close.noop(); // Moves `on_close` inside the closure so it gets destroyed with the closure - tracing::debug!("Received hello: {hello}"); - let _ = || -> jni::errors::Result<()> { - let mut env = java_vm.attach_current_thread_as_daemon()?; - let whatami = hello.whatami() as jint; - let zenoh_id = env - .byte_array_from_slice(&hello.zid().to_le_bytes()) - .map(|it| env.auto_local(it))?; - let locators = env - .new_object("java/util/ArrayList", "()V", &[]) - .map(|it| env.auto_local(it))?; - let jlist = JList::from_env(&mut env, &locators)?; - for value in hello.locators() { - let locator = env.new_string(value.as_str())?; - jlist.add(&mut env, &locator)?; - } - env.call_method( - &callback_global_ref, - "run", - "(I[BLjava/util/List;)V", - &[ - JValue::from(whatami), - JValue::from(&zenoh_id), - JValue::from(&locators), - ], - )?; - Ok(()) - }() - .map_err(|err| tracing::error!("Error while scouting: ${err}")); - }) - .wait() - .map(|scout| Arc::into_raw(Arc::new(scout))) - .map_err(|err| zerror!(err)) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }) -} - -/// Frees the scout. -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIScout_00024Companion_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - scout_ptr: *const Scout<()>, -) { - Arc::from_raw(scout_ptr); -} diff --git a/zenoh-jni/src/session.rs b/zenoh-jni/src/session.rs deleted file mode 100644 index 4c9702f9..00000000 --- a/zenoh-jni/src/session.rs +++ /dev/null @@ -1,1447 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::{mem, ops::Deref, ptr::null, sync::Arc, time::Duration}; - -use jni::{ - objects::{GlobalRef, JByteArray, JClass, JList, JObject, JString, JValue}, - sys::{jboolean, jbyteArray, jint, jlong, jobject}, - JNIEnv, -}; -use zenoh::{ - config::Config, - handlers::Callback, - key_expr::KeyExpr, - pubsub::{Publisher, PublisherBuilder, Subscriber, SubscriberBuilder}, - query::{Querier, Query, Queryable, ReplyError, Selector}, - sample::Sample, - session::{EntityGlobalId, Session, ZenohId}, - Wait, -}; - -use crate::owned_object::OwnedObject; -#[cfg(feature = "zenoh-ext")] -use jni::sys::jdouble; -#[cfg(feature = "zenoh-ext")] -use zenoh_ext::{ - AdvancedPublisher, AdvancedPublisherBuilderExt, AdvancedSubscriber, - AdvancedSubscriberBuilderExt, CacheConfig, HistoryConfig, MissDetectionConfig, RecoveryConfig, - RepliesConfig, -}; - -use crate::{ - errors::ZResult, key_expr::process_kotlin_key_expr, sample_callback::SetJniSampleCallback, - throw_exception, utils::*, zerror, -}; - -/// Open a Zenoh session via JNI. -/// -/// It returns an [Arc] raw pointer to the Zenoh Session, which should be stored as a private read-only attribute -/// of the session object in the Java/Kotlin code. Subsequent calls to other session functions will require -/// this raw pointer to retrieve the [Session] using `Arc::from_raw`. -/// -/// If opening the session fails, an exception is thrown on the JVM, and a null pointer is returned. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class (parameter required by the JNI interface but unused). -/// - `config_path`: Nullable path to the Zenoh config file. If null, the default configuration will be loaded. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_openSessionViaJNI( - mut env: JNIEnv, - _class: JClass, - config_ptr: *const Config, -) -> *const Session { - let session = open_session(config_ptr); - match session { - Ok(session) => Arc::into_raw(Arc::new(session)), - Err(err) => { - tracing::error!("Unable to open session: {}", err); - throw_exception!(env, zerror!(err)); - null() - } - } -} - -/// Open a Zenoh session with the configuration pointed out by `config_path`. -/// -/// If the config path provided is null then the default configuration is loaded. -/// -unsafe fn open_session(config_ptr: *const Config) -> ZResult { - let config = Arc::from_raw(config_ptr); - let result = zenoh::open(config.as_ref().clone()) - .wait() - .map_err(|err| zerror!(err)); - mem::forget(config); - result -} - -/// Open a Zenoh session with a JSON configuration. -/// -/// It returns an [Arc] raw pointer to the Zenoh Session, which should be stored as a private read-only attribute -/// of the session object in the Java/Kotlin code. Subsequent calls to other session functions will require -/// this raw pointer to retrieve the [Session] using `Arc::from_raw`. -/// -/// If opening the session fails, an exception is thrown on the JVM, and a null pointer is returned. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class (parameter required by the JNI interface but unused). -/// - `json_config`: Configuration as a JSON string. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNISession_openSessionWithJsonConfigViaJNI( - mut env: JNIEnv, - _class: JClass, - json_config: JString, -) -> *const Session { - let session = open_session_with_json_config(&mut env, json_config); - match session { - Ok(session) => Arc::into_raw(Arc::new(session)), - Err(err) => { - tracing::error!("Unable to open session: {}", err); - throw_exception!(env, zerror!(err)); - null() - } - } -} - -/// Open a Zenoh session with the provided json configuration. -/// -fn open_session_with_json_config(env: &mut JNIEnv, json_config: JString) -> ZResult { - let json_config = decode_string(env, &json_config)?; - let mut deserializer = - json5::Deserializer::from_str(&json_config).map_err(|err| zerror!(err))?; - let config = Config::from_deserializer(&mut deserializer).map_err(|err| match err { - Ok(c) => zerror!("Invalid configuration: {}", c), - Err(e) => zerror!("JSON error: {}", e), - })?; - zenoh::open(config).wait().map_err(|err| zerror!(err)) -} - -/// Open a Zenoh session with a YAML configuration. -/// -/// It returns an [Arc] raw pointer to the Zenoh Session, which should be stored as a private read-only attribute -/// of the session object in the Java/Kotlin code. Subsequent calls to other session functions will require -/// this raw pointer to retrieve the [Session] using `Arc::from_raw`. -/// -/// If opening the session fails, an exception is thrown on the JVM, and a null pointer is returned. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class (parameter required by the JNI interface but unused). -/// - `yaml_config`: Configuration as a YAML string. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNISession_openSessionWithYamlConfigViaJNI( - mut env: JNIEnv, - _class: JClass, - yaml_config: JString, -) -> *const Session { - let session = open_session_with_yaml_config(&mut env, yaml_config); - match session { - Ok(session) => Arc::into_raw(Arc::new(session)), - Err(err) => { - tracing::error!("Unable to open session: {}", err); - throw_exception!(env, zerror!(err)); - null() - } - } -} - -/// Open a Zenoh session with the provided yaml configuration. -/// -fn open_session_with_yaml_config(env: &mut JNIEnv, yaml_config: JString) -> ZResult { - let yaml_config = decode_string(env, &yaml_config)?; - let deserializer = serde_yaml::Deserializer::from_str(&yaml_config); - let config = Config::from_deserializer(deserializer).map_err(|err| match err { - Ok(c) => zerror!("Invalid configuration: {}", c), - Err(e) => zerror!("YAML error: {}", e), - })?; - zenoh::open(config).wait().map_err(|err| zerror!(err)) -} - -/// Closes a Zenoh session via JNI. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `session_ptr`: The raw pointer to the Zenoh session. -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided session pointer is valid and has not been modified or freed. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// - After the session is closed, the provided pointer is no more valid. -/// -#[no_mangle] -#[allow(non_snake_case, unused)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_closeSessionViaJNI( - mut env: JNIEnv, - _class: JClass, - session_ptr: *const Session, -) { - Arc::from_raw(session_ptr); -} - -/// Declare an advanced Zenoh subscriber via JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `key_expr_ptr`: The key expression pointer for the subscriber. May be null in case of using an -/// undeclared key expression. -/// - `key_expr_str`: String representation of the key expression to be used to declare the subscriber. -/// It won't be considered in case a key_expr_ptr to a declared key expression is provided. -/// - `history_detect_late_publishers` : Enable detection of late joiner publishers and query for their historical data. -/// Late joiner detection can only be achieved for [`AdvancedPublisher`] that enable publisher detection. -/// History can only be retransmitted by [`AdvancedPublisher`] that enable cache. -/// - `history_max_samples` : Specify how many samples to query for each resource. 0 means no limit. -/// - `history_max_age_seconds` : Specify the maximum age of samples to query. <= 0.0 means no limit. -/// - `recovery_config_enabled` : Enable missed samples recovery. -/// - recovery_config_is_heartbeat: If true, use heartbeat mode and subscribe to heartbeats of [`AdvancedPublisher`], -/// if false - enable periodic queries for not yet received Samples. -/// - `recovery_query_period_ms` : Specify period for Periodic queries mode. -/// - `subscriber_detection` : Allow this subscriber to be detected through liveliness. -/// - `session_ptr`: The raw pointer to the Zenoh session. -/// - `callback`: The callback function as an instance of the `JNISubscriberCallback` interface in Java/Kotlin. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called upon closing the subscriber. -/// -/// Returns: -/// - A raw pointer to the declared [AdvancedSubscriber]. In case of failure, an exception is thrown and null is returned. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided [Session] pointer is valid and has not been modified or freed. -/// - The [Session] pointer remains valid and the ownership of the [Session] is not transferred, -/// allowing safe usage of the [Session] after this function call. -/// - The callback function passed as `callback` must be a valid instance of the `JNISubscriberCallback` interface -/// in Java/Kotlin, matching the specified signature. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -#[cfg(feature = "zenoh-ext")] -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareAdvancedSubscriberViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - session_ptr: *const Session, - // HistoryConfig - history_config_enabled: jboolean, - history_detect_late_publishers: jboolean, - history_max_samples: jlong, - history_max_age_seconds: jdouble, - // RecoveryConfig - recovery_config_enabled: jboolean, - recovery_config_is_heartbeat: jboolean, - recovery_query_period_ms: jlong, - - subscriber_detection: jboolean, - - callback: JObject, - on_close: JObject, -) -> *const AdvancedSubscriber<()> { - let session = Arc::from_raw(session_ptr); - let subscriber_ptr = || -> ZResult<*const AdvancedSubscriber<()>> { - let mut builder = prepare_subscriber_builder( - &mut env, - key_expr_ptr, - &key_expr_str, - &session, - callback, - on_close, - "advanced subscriber", - )? - .advanced(); - - if history_config_enabled != 0 { - let mut history = match history_detect_late_publishers != 0 { - true => HistoryConfig::default().detect_late_publishers(), - false => HistoryConfig::default(), - }; - - if history_max_samples > 0 { - history = history.max_samples( - history_max_samples - .try_into() - .map_err(|e: std::num::TryFromIntError| zerror!(e.to_string()))?, - ); - } - - if history_max_age_seconds > 0.0 { - history = history.max_age(history_max_age_seconds); - } - - builder = builder.history(history); - } - - if recovery_config_enabled != 0 { - let recovery = if recovery_config_is_heartbeat != 0 { - RecoveryConfig::default().heartbeat() - } else { - let dur = Duration::from_millis( - recovery_query_period_ms - .try_into() - .map_err(|e: std::num::TryFromIntError| zerror!(e.to_string()))?, - ); - RecoveryConfig::default().periodic_queries(dur) - }; - builder = builder.recovery(recovery); - } - - if subscriber_detection != 0 { - builder = builder.subscriber_detection(); - } - - builder - .wait() - .map(|s| Arc::into_raw(Arc::new(s))) - .map_err(|err| zerror!("Unable to declare advanced subscriber: {}", err)) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }); - std::mem::forget(session); - subscriber_ptr -} - -/// Declare an advanced Zenoh publisher via JNI. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `key_expr_ptr`: Raw pointer to the [KeyExpr] to be used for the publisher, may be null. -/// - `key_expr_str`: String representation of the [KeyExpr] to be used for the publisher. -/// It is only considered when the key_expr_ptr parameter is null, meaning the function is -/// receiving a key expression that was not declared. -/// - `session_ptr`: Raw pointer to the Zenoh [Session] to be used for the publisher. -/// - `congestion_control`: The [zenoh::publisher::CongestionControl] configuration as an ordinal. -/// - `priority`: The [zenoh::core::Priority] configuration as an ordinal. -/// - `is_express`: The express config of the publisher (see [zenoh::prelude::QoSBuilderTrait]). -/// - `reliability`: The reliability value as an ordinal. -/// -/// - `cache_max_samples` : If > 0 - Attach a cache to this [`AdvancedPublisher`] and specify how many samples to keep for each resource. -/// - `cache_replies_priority` : The [zenoh::core::Priority] configuration as an ordinal used for cache replies. -/// - `cache_replies_congestion_control` : The [zenoh::publisher::CongestionControl] configuration as an ordinal used for cache replies. -/// - `cache_replies_is_express: jboolean` : The express config of the publisher (see [zenoh::prelude::QoSBuilderTrait]) used for cache replies. -/// -/// - `sample_miss_detection_enabled` : Enables sample miss detection functionality: allow matching -/// [`AdvancedSubscriber`] to detect lost samples and optionally ask for retransimission. Retransmission can only be achieved if cache is enabled. -/// - `sample_miss_detection_is_not_heartbeat` : Use sample miss detection without heartbeat -/// - `sample_miss_detection_heartbeat_ms` : Specify heartbeat period for heartbeat mode. -/// - `sample_miss_detection_heartbeat_is_sporadic` : determine if heartbeat argument is treated as sporadic -/// -/// # Returns: -/// - A raw pointer to the declared [AdvancedPublisher] or null in case of failure. -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided [Session] pointer is valid and has not been modified or freed. -/// - The ownership of the [Session] is not transferred, and the [Session] pointer remains valid -/// after this function call so it is safe to use it after this call. -/// - The function may throw an exception in case of failure, which should be handled by the caller. -/// -#[cfg(feature = "zenoh-ext")] -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareAdvancedPublisherViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - session_ptr: *const Session, - congestion_control: jint, - priority: jint, - is_express: jboolean, - reliability: jint, - // CacheConfig - cache_enabled: jboolean, - cache_max_samples: jlong, - cache_replies_priority: jint, - cache_replies_congestion_control: jint, - cache_replies_is_express: jboolean, - // MissDetectionConfig - sample_miss_detection_enabled: jboolean, - sample_miss_detection_enable_heartbeat: jboolean, - sample_miss_detection_heartbeat_ms: jlong, - sample_miss_detection_heartbeat_is_sporadic: jboolean, - - publisher_detection: jboolean, -) -> *const AdvancedPublisher<'static> { - let session = OwnedObject::from_raw(session_ptr); - let publisher_ptr = || -> ZResult<*const AdvancedPublisher<'static>> { - let mut builder = prepare_publisher_builder( - &mut env, - key_expr_ptr, - &key_expr_str, - &session, - congestion_control, - priority, - is_express, - reliability, - )? - .advanced(); - - // fill CacheConfig - if cache_enabled != 0 { - let cache_congestion_control = - decode_congestion_control(cache_replies_congestion_control)?; - - let cache_priority = decode_priority(cache_replies_priority)?; - - let replies_config = RepliesConfig::default() - .priority(cache_priority) - .congestion_control(cache_congestion_control) - .express(cache_replies_is_express != 0); - - let cache_config = CacheConfig::default() - .max_samples( - cache_max_samples - .try_into() - .map_err(|e: std::num::TryFromIntError| zerror!(e.to_string()))?, - ) - .replies_config(replies_config); - - builder = builder.cache(cache_config); - } - - // fill MissDetectionConfig - if sample_miss_detection_enabled != 0 { - let miss_detection_config = { - let mut result = MissDetectionConfig::default(); - if sample_miss_detection_enable_heartbeat != 0 { - let duration = Duration::from_millis( - sample_miss_detection_heartbeat_ms - .try_into() - .map_err(|e: std::num::TryFromIntError| zerror!(e.to_string()))?, - ); - - result = match sample_miss_detection_heartbeat_is_sporadic != 0 { - true => result.sporadic_heartbeat(duration), - false => result.heartbeat(duration), - }; - } - result - }; - builder = builder.sample_miss_detection(miss_detection_config); - } - - if publisher_detection != 0 { - builder = builder.publisher_detection(); - } - - let result = builder.wait(); - match result { - Ok(publisher) => Ok(Arc::into_raw(Arc::new(publisher))), - Err(err) => Err(zerror!(err)), - } - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }); - publisher_ptr -} - -/// Declare a Zenoh publisher via JNI. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `key_expr_ptr`: Raw pointer to the [KeyExpr] to be used for the publisher, may be null. -/// - `key_expr_str`: String representation of the [KeyExpr] to be used for the publisher. -/// It is only considered when the key_expr_ptr parameter is null, meaning the function is -/// receiving a key expression that was not declared. -/// - `session_ptr`: Raw pointer to the Zenoh [Session] to be used for the publisher. -/// - `congestion_control`: The [zenoh::publisher::CongestionControl] configuration as an ordinal. -/// - `priority`: The [zenoh::core::Priority] configuration as an ordinal. -/// - `is_express`: The express config of the publisher (see [zenoh::prelude::QoSBuilderTrait]). -/// - `reliability`: The reliability value as an ordinal. -/// -/// # Returns: -/// - A raw pointer to the declared Zenoh publisher or null in case of failure. -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided session pointer is valid and has not been modified or freed. -/// - The ownership of the session is not transferred, and the session pointer remains valid -/// after this function call so it is safe to use it after this call. -/// - The function may throw an exception in case of failure, which should be handled by the caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declarePublisherViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - session_ptr: *const Session, - congestion_control: jint, - priority: jint, - is_express: jboolean, - reliability: jint, -) -> *const Publisher<'static> { - let session = Arc::from_raw(session_ptr); - let publisher_ptr = || -> ZResult<*const Publisher<'static>> { - prepare_publisher_builder( - &mut env, - key_expr_ptr, - &key_expr_str, - &session, - congestion_control, - priority, - is_express, - reliability, - )? - .wait() - .map(|publisher| Arc::into_raw(Arc::new(publisher))) - .map_err(|e| zerror!(e)) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }); - std::mem::forget(session); - publisher_ptr -} - -#[allow(clippy::too_many_arguments)] -unsafe fn prepare_publisher_builder<'a, 'b>( - env: &mut JNIEnv, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: &JString, - session: &'a Session, - congestion_control: jint, - priority: jint, - is_express: jboolean, - reliability: jint, -) -> ZResult> { - let key_expr = process_kotlin_key_expr(env, key_expr_str, key_expr_ptr)?; - let congestion_control = decode_congestion_control(congestion_control)?; - let priority = decode_priority(priority)?; - let reliability = decode_reliability(reliability)?; - let builder = session - .declare_publisher(key_expr) - .congestion_control(congestion_control) - .priority(priority) - .express(is_express != 0) - .reliability(reliability); - Ok(builder) -} - -/// Performs a `put` operation in the Zenoh session via JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `key_expr_ptr`: Raw pointer to the [KeyExpr] to be used for the operation, may be null. -/// - `key_expr_str`: String representation of the [KeyExpr] to be used for the operation. -/// It is only considered when the key_expr_ptr parameter is null, meaning the function is -/// receiving a key expression that was not declared. -/// - `session_ptr`: Raw pointer to the [Session] to be used for the operation. -/// - `payload`: The payload to send through the network. -/// - `encoding_id`: The encoding id of the payload. -/// - `encoding_schema`: Optional encoding schema, may be null. -/// - `congestion_control`: The [CongestionControl] mechanism specified. -/// - `priority`: The [Priority] mechanism specified. -/// - `is_express`: The express flag. -/// - `attachment`: Optional attachment encoded into a byte array. May be null. -/// - `reliability`: The reliability value as an ordinal. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided session pointer is valid and has not been modified or freed. -/// - The session pointer remains valid and the ownership of the session is not transferred, -/// allowing safe usage of the session after this function call. -/// - The function may throw an exception in case of failure, which should be handled by the Java/Kotlin caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_putViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - session_ptr: *const Session, - payload: JByteArray, - encoding_id: jint, - encoding_schema: JString, - congestion_control: jint, - priority: jint, - is_express: jboolean, - attachment: JByteArray, - reliability: jint, -) { - let session = Arc::from_raw(session_ptr); - let _ = || -> ZResult<()> { - let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; - let payload = decode_byte_array(&env, payload)?; - let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; - let congestion_control = decode_congestion_control(congestion_control)?; - let priority = decode_priority(priority)?; - let reliability = decode_reliability(reliability)?; - - let mut put_builder = session - .put(&key_expr, payload) - .congestion_control(congestion_control) - .encoding(encoding) - .express(is_express != 0) - .priority(priority) - .reliability(reliability); - - if !attachment.is_null() { - let attachment = decode_byte_array(&env, attachment)?; - put_builder = put_builder.attachment(attachment) - } - - put_builder - .wait() - .map(|_| tracing::trace!("Put on '{key_expr}'")) - .map_err(|err| zerror!(err)) - }() - .map_err(|err| throw_exception!(env, err)); - std::mem::forget(session); -} - -/// Performs a `delete` operation in the Zenoh session via JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `key_expr_ptr`: Raw pointer to the [KeyExpr] to be used for the operation, may be null. -/// - `key_expr_str`: String representation of the [KeyExpr] to be used for the operation. -/// It is only considered when the key_expr_ptr parameter is null, meaning the function is -/// receiving a key expression that was not declared. -/// - `session_ptr`: Raw pointer to the [Session] to be used for the operation. -/// - `congestion_control`: The [CongestionControl] mechanism specified. -/// - `priority`: The [Priority] mechanism specified. -/// - `is_express`: The express flag. -/// - `attachment`: Optional attachment encoded into a byte array. May be null. -/// - `reliability`: The reliability value as an ordinal. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided session pointer is valid and has not been modified or freed. -/// - The session pointer remains valid and the ownership of the session is not transferred, -/// allowing safe usage of the session after this function call. -/// - The function may throw a JNI exception or a Session exception in case of failure, which -/// should be handled by the Java/Kotlin caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_deleteViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - session_ptr: *const Session, - congestion_control: jint, - priority: jint, - is_express: jboolean, - attachment: JByteArray, - reliability: jint, -) { - let session = Arc::from_raw(session_ptr); - let _ = || -> ZResult<()> { - let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; - let congestion_control = decode_congestion_control(congestion_control)?; - let priority = decode_priority(priority)?; - let reliability = decode_reliability(reliability)?; - - let mut delete_builder = session - .delete(&key_expr) - .congestion_control(congestion_control) - .express(is_express != 0) - .priority(priority) - .reliability(reliability); - - if !attachment.is_null() { - let attachment = decode_byte_array(&env, attachment)?; - delete_builder = delete_builder.attachment(attachment) - } - - delete_builder - .wait() - .map(|_| tracing::trace!("Delete on '{key_expr}'")) - .map_err(|err| zerror!(err)) - }() - .map_err(|err| throw_exception!(env, err)); - std::mem::forget(session); -} - -/// Declare a Zenoh subscriber via JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `key_expr_ptr`: The key expression pointer for the subscriber. May be null in case of using an -/// undeclared key expression. -/// - `key_expr_str`: String representation of the key expression to be used to declare the subscriber. -/// It won't be considered in case a key_expr_ptr to a declared key expression is provided. -/// - `session_ptr`: The raw pointer to the Zenoh session. -/// - `callback`: The callback function as an instance of the `JNISubscriberCallback` interface in Java/Kotlin. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called upon closing the subscriber. -/// -/// Returns: -/// - A raw pointer to the declared Zenoh subscriber. In case of failure, an exception is thrown and null is returned. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided session pointer is valid and has not been modified or freed. -/// - The session pointer remains valid and the ownership of the session is not transferred, -/// allowing safe usage of the session after this function call. -/// - The callback function passed as `callback` must be a valid instance of the `JNISubscriberCallback` interface -/// in Java/Kotlin, matching the specified signature. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareSubscriberViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - session_ptr: *const Session, - callback: JObject, - on_close: JObject, -) -> *const Subscriber<()> { - let session = Arc::from_raw(session_ptr); - let subscriber_ptr = || -> ZResult<*const Subscriber<()>> { - prepare_subscriber_builder( - &mut env, - key_expr_ptr, - &key_expr_str, - &session, - callback, - on_close, - "subscriber", - )? - .wait() - .map(|s| Arc::into_raw(Arc::new(s))) - .map_err(|err| zerror!("Unable to declare subscriber: {}", err)) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }); - std::mem::forget(session); - subscriber_ptr -} - -unsafe fn prepare_subscriber_builder<'a, 'b>( - env: &mut JNIEnv, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: &JString, - session: &'a Session, - callback: JObject, - on_close: JObject, - entity_name: &str, -) -> ZResult>> { - let key_expr = process_kotlin_key_expr(env, key_expr_str, key_expr_ptr)?; - tracing::debug!("Declaring {entity_name} on '{}'...", key_expr); - - let builder = session - .declare_subscriber(key_expr.to_owned()) - .set_jni_sample_callback(env, callback, on_close)?; - - tracing::debug!("{entity_name} declared on '{}'.", key_expr); - Ok(builder) -} - -/// Declare a Zenoh querier via JNI. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `key_expr_ptr`: A raw pointer to the [KeyExpr] to be used for the querier. May be null in case of using an -/// undeclared key expression. -/// - `key_expr_str`: String representation of the key expression to be used to declare the querier. -/// It won't be considered in case a key_expr_ptr to a declared key expression is provided. -/// - `target`: The ordinal value of the query target enum value. -/// - `consolidation`: The ordinal value of the consolidation enum value. -/// - `congestion_control`: The ordinal value of the congestion control enum value. -/// - `priority`: The ordinal value of the priority enum value. -/// - `is_express`: The boolean express value of the QoS provided. -/// - `timeout_ms`: The timeout in milliseconds. -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareQuerierViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - session_ptr: *const Session, - target: jint, - consolidation: jint, - congestion_control: jint, - priority: jint, - is_express: jboolean, - timeout_ms: jlong, - accept_replies: jint, -) -> *const Querier<'static> { - let session = Arc::from_raw(session_ptr); - || -> ZResult<*const Querier<'static>> { - let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; - let query_target = decode_query_target(target)?; - let consolidation = decode_consolidation(consolidation)?; - let congestion_control = decode_congestion_control(congestion_control)?; - let timeout = Duration::from_millis(timeout_ms as u64); - let priority = decode_priority(priority)?; - let reply_key_expr = decode_reply_key_expr(accept_replies)?; - tracing::debug!("Declaring querier on '{}'...", key_expr); - - let querier = session - .declare_querier(key_expr.to_owned()) - .congestion_control(congestion_control) - .consolidation(consolidation) - .express(is_express != 0) - .target(query_target) - .priority(priority) - .timeout(timeout) - .accept_replies(reply_key_expr) - .wait() - .map_err(|err| zerror!(err))?; - - tracing::debug!("Querier declared on '{}'.", key_expr); - std::mem::forget(session); - Ok(Arc::into_raw(Arc::new(querier))) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }) -} - -/// Declare a Zenoh queryable via JNI. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `key_expr_ptr`: A raw pointer to the [KeyExpr] to be used for the queryable. May be null in case of using an -/// undeclared key expression. -/// - `key_expr_str`: String representation of the key expression to be used to declare the queryable. -/// It won't be considered in case a key_expr_ptr to a declared key expression is provided. -/// - `session_ptr`: A raw pointer to the Zenoh [Session] to be used to declare the queryable. -/// - `callback`: The callback function as an instance of the `JNIQueryableCallback` interface in Java/Kotlin. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called upon closing the queryable. -/// - `complete`: The completeness of the queryable. -/// -/// Returns: -/// - A raw pointer to the declared Zenoh queryable. In case of failure, an exception is thrown and null is returned. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided session pointer is valid and has not been modified or freed. -/// - The session pointer remains valid and the ownership of the session is not transferred, -/// allowing safe usage of the session after this function call. -/// - The callback function passed as `callback` must be a valid instance of the `JNIQueryableCallback` interface -/// in Java/Kotlin, matching the specified signature. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareQueryableViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - session_ptr: *const Session, - callback: JObject, - on_close: JObject, - complete: jboolean, -) -> *const Queryable<()> { - let session = Arc::from_raw(session_ptr); - let query_ptr = || -> ZResult<*const Queryable<()>> { - let java_vm = Arc::new(get_java_vm(&mut env)?); - let callback_global_ref = get_callback_global_ref(&mut env, callback)?; - let on_close_global_ref = get_callback_global_ref(&mut env, on_close)?; - let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; - let complete = complete != 0; - let on_close = load_on_close(&java_vm, on_close_global_ref); - tracing::debug!("Declaring queryable through JNI on {}", key_expr); - let builder = session - .declare_queryable(key_expr) - .callback(move |query: Query| { - on_close.noop(); // Does nothing, but moves `on_close` inside the closure so it gets destroyed with the closure - let env = match java_vm.attach_current_thread_as_daemon() { - Ok(env) => env, - Err(err) => { - tracing::error!("Unable to attach thread for queryable callback: {}", err); - return; - } - }; - - tracing::debug!("Receiving query through JNI: {}", query.to_string()); - match on_query(env, query, &callback_global_ref) { - Ok(_) => tracing::debug!("Queryable callback called successfully."), - Err(err) => tracing::error!("Error calling queryable callback: {}", err), - } - }) - .complete(complete); - - let queryable = builder - .wait() - .map_err(|err| zerror!("Error declaring queryable: {}", err))?; - Ok(Arc::into_raw(Arc::new(queryable))) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }); - std::mem::forget(session); - query_ptr -} - -fn on_query(mut env: JNIEnv, query: Query, callback_global_ref: &GlobalRef) -> ZResult<()> { - let selector_params_jstr = env - .new_string(query.parameters().to_string()) - .map(|value| env.auto_local(value)) - .map_err(|err| { - zerror!( - "Could not create a JString through JNI for the Query key expression. {}", - err - ) - })?; - - let (payload, encoding_id, encoding_schema) = if let Some(payload) = query.payload() { - let encoding = query.encoding().unwrap(); //If there is payload, there is encoding. - let encoding_id = encoding.id() as jint; - let encoding_schema = encoding - .schema() - .map_or_else( - || Ok(JString::default()), - |schema| slice_to_java_string(&env, schema), - ) - .map(|value| env.auto_local(value))?; - let byte_array = bytes_to_java_array(&env, payload).map(|value| env.auto_local(value))?; - (byte_array, encoding_id, encoding_schema) - } else { - ( - env.auto_local(JByteArray::default()), - 0, - env.auto_local(JString::default()), - ) - }; - - let attachment_bytes = query - .attachment() - .map_or_else( - || Ok(JByteArray::default()), - |attachment| bytes_to_java_array(&env, attachment), - ) - .map(|value| env.auto_local(value)) - .map_err(|err| zerror!("Error processing attachment of reply: {}.", err))?; - - let key_expr_str = env - .new_string(query.key_expr().to_string()) - .map(|key_expr| env.auto_local(key_expr)) - .map_err(|err| { - zerror!( - "Could not create a JString through JNI for the Query key expression: {}.", - err - ) - })?; - - let query_ptr = Arc::into_raw(Arc::new(query)); - - let result = env - .call_method( - callback_global_ref, - "run", - "(Ljava/lang/String;Ljava/lang/String;[BILjava/lang/String;[BJ)V", - &[ - JValue::from(&key_expr_str), - JValue::from(&selector_params_jstr), - JValue::from(&payload), - JValue::from(encoding_id), - JValue::from(&encoding_schema), - JValue::from(&attachment_bytes), - JValue::from(query_ptr as jlong), - ], - ) - .map(|_| ()) - .map_err(|err| { - // The callback could not be invoked, therefore the created kotlin query object won't be - // used. Since `query_ptr` as well as `key_expr_ptr` was created within this function - // and remains unaltered, it is safe to reclaim ownership of the memory by converting - // the raw pointers back into an `Arc` and freeing the memory. - unsafe { - Arc::from_raw(query_ptr); - }; - _ = env.exception_describe(); - zerror!(err) - }); - result -} - -/// Declare a [KeyExpr] through a [Session] via JNI. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `session_ptr`: A raw pointer to the Zenoh [Session] from which to declare the key expression. -/// - `key_expr_str`: A Java String with the intended key expression. -/// -/// # Returns: -/// - A raw pointer to the declared key expression. In case of failure, an exception is thrown and null is returned. -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided session pointer is valid and has not been modified or freed. -/// - The session pointer remains valid and the ownership of the session is not transferred, -/// allowing safe usage of the session after this function call. -/// - The function may throw an exception in case of failure, which should be handled by the caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareKeyExprViaJNI( - mut env: JNIEnv, - _class: JClass, - session_ptr: *const Session, - key_expr_str: JString, -) -> *const KeyExpr<'static> { - let session: Arc = Arc::from_raw(session_ptr); - let key_expr_ptr = || -> ZResult<*const KeyExpr<'static>> { - let key_expr_str = decode_string(&mut env, &key_expr_str)?; - let key_expr = session - .declare_keyexpr(key_expr_str.to_owned()) - .wait() - .map_err(|err| { - zerror!( - "Unable to declare key expression '{}': {}", - key_expr_str, - err - ) - })?; - Ok(Arc::into_raw(Arc::new(key_expr))) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - null() - }); - mem::forget(session); - key_expr_ptr -} - -/// Undeclare a [KeyExpr] through a [Session] via JNI. -/// -/// The key expression must have been previously declared on the specified session, otherwise an -/// exception is thrown. -/// -/// This functions frees the key expression pointer provided. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `session_ptr`: A raw pointer to the Zenoh [Session] from which to undeclare the key expression. -/// - `key_expr_ptr`: A raw pointer to the [KeyExpr] to undeclare. -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided session and keyexpr pointers are valid and have not been modified or freed. -/// - The session pointer remains valid after this function call. -/// - The key expression pointer is voided after this function call. -/// - The function may throw an exception in case of failure, which should be handled by the caller. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_undeclareKeyExprViaJNI( - mut env: JNIEnv, - _class: JClass, - session_ptr: *const Session, - key_expr_ptr: *const KeyExpr<'static>, -) { - let session = Arc::from_raw(session_ptr); - let key_expr = Arc::from_raw(key_expr_ptr); - let key_expr_clone = key_expr.deref().clone(); - match session.undeclare(key_expr_clone).wait() { - Ok(_) => {} - Err(err) => { - throw_exception!( - env, - zerror!("Unable to declare key expression '{}': {}", key_expr, err) - ); - } - } - std::mem::forget(session); - // `key_expr` is intentionally left to be freed by Rust -} - -/// Performs a `get` operation in the Zenoh session via JNI with Value. -/// -/// # Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `key_expr_ptr`: Raw pointer to a declared [KeyExpr] to be used for the query. May be null in case -/// of using a non declared key expression, in which case the `key_expr_str` parameter will be used instead. -/// - `key_expr_str`: String representation of the key expression to be used to declare the query. It is not -/// considered if a `key_expr_ptr` is provided. -/// - `selector_params`: Optional parameters of the selector. -/// - `session_ptr`: A raw pointer to the Zenoh [Session]. -/// - `callback`: A Java/Kotlin callback to be called upon receiving a reply. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called when no more replies will be received. -/// - `timeout_ms`: The timeout in milliseconds. -/// - `target`: The query target as the ordinal of the enum. -/// - `consolidation`: The consolidation mode as the ordinal of the enum. -/// - `attachment`: An optional attachment encoded into a byte array. -/// - `payload`: Optional payload for the query. -/// - `encoding_id`: The encoding of the payload. -/// - `encoding_schema`: The encoding schema of the payload, may be null. -/// - `congestion_control`: The ordinal value of the congestion control enum value. -/// - `priority`: The ordinal value of the priority enum value. -/// - `is_express`: The boolean express value of the QoS provided. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided session pointer is valid and has not been modified or freed. -/// - The session pointer remains valid and the ownership of the session is not transferred, -/// allowing safe usage of the session after this function call. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -/// Throws: -/// - An exception in case of failure handling the query. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_getViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, - key_expr_str: JString, - selector_params: /*nullable*/ JString, - session_ptr: *const Session, - callback: JObject, - on_close: JObject, - timeout_ms: jlong, - target: jint, - consolidation: jint, - attachment: /*nullable*/ JByteArray, - payload: /*nullable*/ JByteArray, - encoding_id: jint, - encoding_schema: /*nullable*/ JString, - congestion_control: jint, - priority: jint, - is_express: jboolean, - accept_replies: jint, -) { - let session = Arc::from_raw(session_ptr); - let _ = || -> ZResult<()> { - let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; - let java_vm = Arc::new(get_java_vm(&mut env)?); - let callback_global_ref = get_callback_global_ref(&mut env, callback)?; - let on_close_global_ref = get_callback_global_ref(&mut env, on_close)?; - let query_target = decode_query_target(target)?; - let consolidation = decode_consolidation(consolidation)?; - let timeout = Duration::from_millis(timeout_ms as u64); - let congestion_control = decode_congestion_control(congestion_control)?; - let priority = decode_priority(priority)?; - let reply_key_expr = decode_reply_key_expr(accept_replies)?; - let on_close = load_on_close(&java_vm, on_close_global_ref); - let selector_params = if selector_params.is_null() { - String::new() - } else { - decode_string(&mut env, &selector_params)? - }; - let selector = Selector::owned(&key_expr, selector_params); - let mut get_builder = session - .get(selector) - .congestion_control(congestion_control) - .priority(priority) - .express(is_express != 0) - .callback(move |reply| { - || -> ZResult<()> { - on_close.noop(); // Does nothing, but moves `on_close` inside the closure so it gets destroyed with the closure - tracing::debug!("Receiving reply through JNI: {:?}", reply); - let mut env = java_vm.attach_current_thread_as_daemon().map_err(|err| { - zerror!("Unable to attach thread for GET query callback: {}.", err) - })?; - - match reply.result() { - Ok(sample) => on_reply_success( - &mut env, - reply.replier_id(), - sample, - &callback_global_ref, - ), - Err(error) => on_reply_error( - &mut env, - reply.replier_id(), - error, - &callback_global_ref, - ), - } - }() - .unwrap_or_else(|err| tracing::error!("Error on get callback: {err}")); - }) - .target(query_target) - .timeout(timeout) - .consolidation(consolidation) - .accept_replies(reply_key_expr); - - if !payload.is_null() { - let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; - get_builder = get_builder.encoding(encoding); - get_builder = get_builder.payload(decode_byte_array(&env, payload)?); - } - - if !attachment.is_null() { - let attachment = decode_byte_array(&env, attachment)?; - get_builder = get_builder.attachment::>(attachment); - } - - get_builder - .wait() - .map(|_| tracing::trace!("Performing get on '{key_expr}'.",)) - .map_err(|err| zerror!(err)) - }() - .map_err(|err| throw_exception!(env, err)); - std::mem::forget(session); -} - -pub(crate) fn on_reply_success( - env: &mut JNIEnv, - replier_id: Option, - sample: &Sample, - callback_global_ref: &GlobalRef, -) -> ZResult<()> { - let zenoh_id = replier_id - .map_or_else( - || Ok(JByteArray::default()), - |replier_id| { - env.byte_array_from_slice(&replier_id.zid().to_le_bytes()) - .map_err(|err| zerror!(err)) - }, - ) - .map(|value| env.auto_local(value))?; - let eid = replier_id.map_or_else(|| 0, |replier_id| replier_id.eid() as jint); - - let byte_array = - bytes_to_java_array(env, sample.payload()).map(|value| env.auto_local(value))?; - let encoding: jint = sample.encoding().id() as jint; - let encoding_schema = sample - .encoding() - .schema() - .map_or_else( - || Ok(JString::default()), - |schema| slice_to_java_string(env, schema), - ) - .map(|value| env.auto_local(value))?; - let kind = sample.kind() as jint; - - let (timestamp, is_valid) = sample - .timestamp() - .map(|timestamp| (timestamp.get_time().as_u64(), true)) - .unwrap_or((0, false)); - - let attachment_bytes = sample - .attachment() - .map_or_else( - || Ok(JByteArray::default()), - |attachment| bytes_to_java_array(env, attachment), - ) - .map(|value| env.auto_local(value)) - .map_err(|err| zerror!("Error processing attachment of reply: {}.", err))?; - - let key_expr_str = env - .new_string(sample.key_expr().to_string()) - .map(|value| env.auto_local(value)) - .map_err(|err| { - zerror!( - "Could not create a JString through JNI for the Sample key expression. {}", - err - ) - })?; - - let express = sample.express(); - let priority = sample.priority() as jint; - let cc = sample.congestion_control() as jint; - - let result = match env.call_method( - callback_global_ref, - "run", - "([BIZLjava/lang/String;[BILjava/lang/String;IJZ[BZII)V", - &[ - JValue::from(&zenoh_id), - JValue::from(eid), - JValue::from(true), - JValue::from(&key_expr_str), - JValue::from(&byte_array), - JValue::from(encoding), - JValue::from(&encoding_schema), - JValue::from(kind), - JValue::from(timestamp as i64), - JValue::from(is_valid), - JValue::from(&attachment_bytes), - JValue::from(express), - JValue::from(priority), - JValue::from(cc), - ], - ) { - Ok(_) => Ok(()), - Err(err) => { - _ = env.exception_describe(); - Err(zerror!("On GET callback error: {}", err)) - } - }; - result -} - -pub(crate) fn on_reply_error( - env: &mut JNIEnv, - replier_id: Option, - reply_error: &ReplyError, - callback_global_ref: &GlobalRef, -) -> ZResult<()> { - let zenoh_id = replier_id - .map_or_else( - || Ok(JByteArray::default()), - |replier_id| { - env.byte_array_from_slice(&replier_id.zid().to_le_bytes()) - .map_err(|err| zerror!(err)) - }, - ) - .map(|value| env.auto_local(value))?; - let eid = replier_id.map_or_else(|| 0, |replier_id| replier_id.eid() as jint); - - let payload = - bytes_to_java_array(env, reply_error.payload()).map(|value| env.auto_local(value))?; - let encoding_id: jint = reply_error.encoding().id() as jint; - let encoding_schema = reply_error - .encoding() - .schema() - .map_or_else( - || Ok(JString::default()), - |schema| slice_to_java_string(env, schema), - ) - .map(|value| env.auto_local(value))?; - let result = match env.call_method( - callback_global_ref, - "run", - "([BIZLjava/lang/String;[BILjava/lang/String;IJZ[BZII)V", - &[ - JValue::from(&zenoh_id), - JValue::from(eid), - JValue::from(false), - JValue::from(&JString::default()), - JValue::from(&payload), - JValue::from(encoding_id), - JValue::from(&encoding_schema), - // The remaining parameters aren't used in case of replying error, so we set them to default. - JValue::from(0 as jint), - JValue::from(0_i64), - JValue::from(false), - JValue::from(&JByteArray::default()), - JValue::from(false), - JValue::from(0 as jint), - JValue::from(0 as jint), - ], - ) { - Ok(_) => Ok(()), - Err(err) => { - _ = env.exception_describe(); - Err(zerror!("On GET callback error: {}", err)) - } - }; - result -} - -/// Returns a list of zenoh ids as byte arrays corresponding to the peers connected to the session provided. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_getPeersZidViaJNI( - mut env: JNIEnv, - _class: JClass, - session_ptr: *const Session, -) -> jobject { - let session = Arc::from_raw(session_ptr); - let ids = { - let peers_zid = session.info().peers_zid().wait(); - let ids = peers_zid.collect::>(); - ids_to_java_list(&mut env, ids).map_err(|err| zerror!(err)) - } - .unwrap_or_else(|err| { - throw_exception!(env, err); - JObject::default().as_raw() - }); - std::mem::forget(session); - ids -} - -/// Returns a list of zenoh ids as byte arrays corresponding to the routers connected to the session provided. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_getRoutersZidViaJNI( - mut env: JNIEnv, - _class: JClass, - session_ptr: *const Session, -) -> jobject { - let session = Arc::from_raw(session_ptr); - let ids = { - let peers_zid = session.info().routers_zid().wait(); - let ids = peers_zid.collect::>(); - ids_to_java_list(&mut env, ids).map_err(|err| zerror!(err)) - } - .unwrap_or_else(|err| { - throw_exception!(env, err); - JObject::default().as_raw() - }); - std::mem::forget(session); - ids -} - -/// Returns the Zenoh ID as a byte array of the session. -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_getZidViaJNI( - mut env: JNIEnv, - _class: JClass, - session_ptr: *const Session, -) -> jbyteArray { - let session = Arc::from_raw(session_ptr); - let ids = { - let zid = session.info().zid().wait(); - env.byte_array_from_slice(&zid.to_le_bytes()) - .map(|x| x.as_raw()) - .map_err(|err| zerror!(err)) - } - .unwrap_or_else(|err| { - throw_exception!(env, err); - JByteArray::default().as_raw() - }); - std::mem::forget(session); - ids -} - -fn ids_to_java_list(env: &mut JNIEnv, ids: Vec) -> jni::errors::Result { - let array_list = env.new_object("java/util/ArrayList", "()V", &[])?; - let jlist = JList::from_env(env, &array_list)?; - for id in ids { - let value = &mut env.byte_array_from_slice(&id.to_le_bytes())?; - jlist.add(env, value)?; - } - Ok(array_list.as_raw()) -} diff --git a/zenoh-jni/src/subscriber.rs b/zenoh-jni/src/subscriber.rs deleted file mode 100644 index 3462ec7e..00000000 --- a/zenoh-jni/src/subscriber.rs +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use jni::{objects::JClass, JNIEnv}; -use zenoh::pubsub::Subscriber; - -/// Frees the [Subscriber]. -/// -/// # Parameters: -/// - `_env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `subscriber_ptr`: The raw pointer to the Zenoh subscriber ([Subscriber]). -/// -/// # Safety: -/// - The function is marked as unsafe due to raw pointer manipulation. -/// - It assumes that the provided subscriber pointer is valid and has not been modified or freed. -/// - The function takes ownership of the raw pointer and releases the associated memory. -/// - After calling this function, the subscriber pointer becomes invalid and should not be used anymore. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNISubscriber_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - subscriber_ptr: *const Subscriber<()>, -) { - Arc::from_raw(subscriber_ptr); -} diff --git a/zenoh-jni/src/utils.rs b/zenoh-jni/src/utils.rs deleted file mode 100644 index e60add13..00000000 --- a/zenoh-jni/src/utils.rs +++ /dev/null @@ -1,189 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use crate::{errors::ZResult, throw_exception, zerror}; -use jni::{ - objects::{JByteArray, JObject, JString}, - sys::jint, - JNIEnv, JavaVM, -}; -use zenoh::{ - bytes::{Encoding, ZBytes}, - internal::buffers::ZSlice, - qos::{CongestionControl, Priority, Reliability}, - query::{ConsolidationMode, QueryTarget, ReplyKeyExpr}, -}; - -/// Converts a JString into a rust String. -pub(crate) fn decode_string(env: &mut JNIEnv, string: &JString) -> ZResult { - let binding = env - .get_string(string) - .map_err(|err| zerror!("Error while retrieving JString: {}", err))?; - let value = binding - .to_str() - .map_err(|err| zerror!("Error decoding JString: {}", err))?; - Ok(value.to_string()) -} - -pub(crate) fn decode_encoding( - env: &mut JNIEnv, - encoding: jint, - schema: &JString, -) -> ZResult { - let schema: Option = if schema.is_null() { - None - } else { - Some(decode_string(env, schema)?.into_bytes().into()) - }; - let encoding_id = - u16::try_from(encoding).map_err(|err| zerror!("Failed to decode encoding: {}", err))?; - Ok(Encoding::new(encoding_id, schema)) -} - -pub(crate) fn get_java_vm(env: &mut JNIEnv) -> ZResult { - env.get_java_vm() - .map_err(|err| zerror!("Unable to retrieve JVM reference: {}", err)) -} - -pub(crate) fn get_callback_global_ref( - env: &mut JNIEnv, - callback: JObject, -) -> crate::errors::ZResult { - env.new_global_ref(callback) - .map_err(|err| zerror!("Unable to get reference to the provided callback: {}", err)) -} - -/// Helper function to convert a JByteArray into a Vec. -pub(crate) fn decode_byte_array(env: &JNIEnv<'_>, payload: JByteArray) -> ZResult> { - let payload_len = env - .get_array_length(&payload) - .map(|length| length as usize) - .map_err(|err| zerror!(err))?; - let mut buff = vec![0; payload_len]; - env.get_byte_array_region(payload, 0, &mut buff[..]) - .map_err(|err| zerror!(err))?; - let buff: Vec = unsafe { std::mem::transmute::, Vec>(buff) }; - Ok(buff) -} - -pub(crate) fn decode_priority(priority: jint) -> ZResult { - Priority::try_from(priority as u8).map_err(|err| zerror!("Error retrieving priority: {}.", err)) -} - -pub(crate) fn decode_congestion_control(congestion_control: jint) -> ZResult { - match congestion_control { - 1 => Ok(CongestionControl::Block), - 0 => Ok(CongestionControl::Drop), - value => Err(zerror!("Unknown congestion control '{}'.", value)), - } -} - -pub(crate) fn decode_query_target(target: jint) -> ZResult { - match target { - 0 => Ok(QueryTarget::BestMatching), - 1 => Ok(QueryTarget::All), - 2 => Ok(QueryTarget::AllComplete), - value => Err(zerror!("Unable to decode QueryTarget '{}'.", value)), - } -} - -pub(crate) fn decode_reply_key_expr(reply_key_expr: jint) -> ZResult { - match reply_key_expr { - 0 => Ok(ReplyKeyExpr::Any), - 1 => Ok(ReplyKeyExpr::MatchingQuery), - value => Err(zerror!("Unable to decode ReplyKeyExpr '{}'.", value)), - } -} - -pub(crate) fn decode_consolidation(consolidation: jint) -> ZResult { - match consolidation { - 0 => Ok(ConsolidationMode::Auto), - 1 => Ok(ConsolidationMode::None), - 2 => Ok(ConsolidationMode::Monotonic), - 3 => Ok(ConsolidationMode::Latest), - value => Err(zerror!("Unable to decode consolidation '{}'", value)), - } -} - -pub(crate) fn decode_reliability(reliability: jint) -> ZResult { - match reliability { - 0 => Ok(Reliability::BestEffort), - 1 => Ok(Reliability::Reliable), - value => Err(zerror!("Unable to decode reliability '{}'", value)), - } -} - -pub(crate) fn bytes_to_java_array<'a>(env: &JNIEnv<'a>, slice: &ZBytes) -> ZResult> { - env.byte_array_from_slice(&slice.to_bytes()) - .map_err(|err| zerror!(err)) -} - -pub(crate) fn slice_to_java_string<'a>(env: &JNIEnv<'a>, slice: &ZSlice) -> ZResult> { - env.new_string( - String::from_utf8(slice.to_vec()) - .map_err(|err| zerror!("Unable to decode string: {}", err))?, - ) - .map_err(|err| zerror!(err)) -} - -/// A type that calls a function when dropped -pub(crate) struct CallOnDrop(core::mem::MaybeUninit); -impl CallOnDrop { - /// Constructs a value that calls `f` when dropped. - pub fn new(f: F) -> Self { - Self(core::mem::MaybeUninit::new(f)) - } - /// Does nothing, but tricks closures into moving the value inside, - /// so that the closure's destructor will call `drop(self)`. - pub fn noop(&self) {} -} -impl Drop for CallOnDrop { - fn drop(&mut self) { - // Take ownership of the closure that is always initialized, - // since the only constructor uses `MaybeUninit::new` - let f = unsafe { self.0.assume_init_read() }; - // Call the now owned function - f(); - } -} - -pub(crate) fn load_on_close( - java_vm: &Arc, - on_close_global_ref: jni::objects::GlobalRef, -) -> CallOnDrop { - CallOnDrop::new({ - let java_vm = java_vm.clone(); - move || { - let mut env = match java_vm.attach_current_thread_as_daemon() { - Ok(env) => env, - Err(err) => { - tracing::error!("Unable to attach thread for 'onClose' callback: {}", err); - return; - } - }; - match env.call_method(on_close_global_ref, "run", "()V", &[]) { - Ok(_) => (), - Err(err) => { - _ = env.exception_describe(); - throw_exception!( - env, - zerror!("Error while running 'onClose' callback: {}", err) - ); - } - } - } - }) -} diff --git a/zenoh-jni/src/zbytes.rs b/zenoh-jni/src/zbytes.rs deleted file mode 100644 index 0a1b0443..00000000 --- a/zenoh-jni/src/zbytes.rs +++ /dev/null @@ -1,571 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use jni::{ - objects::{AutoLocal, JByteArray, JClass, JList, JMap, JObject, JString, JValue}, - sys::jobject, - JNIEnv, -}; -use zenoh::bytes::ZBytes; -use zenoh_ext::{VarInt, ZDeserializeError, ZDeserializer, ZSerializer}; - -use crate::{ - errors::ZResult, - throw_exception, - utils::{bytes_to_java_array, decode_byte_array}, - zerror, -}; - -enum KotlinType { - Boolean, - String, - ByteArray, - Byte, - Short, - Int, - Long, - Float, - Double, - UByte, - UShort, - UInt, - ULong, - List(Box), - Map(Box, Box), - Pair(Box, Box), - Triple(Box, Box, Box), -} - -fn decode_ktype(env: &mut JNIEnv, ktype: JObject) -> ZResult { - let classifier_obj = env - .call_method( - &ktype, - "getClassifier", - "()Lkotlin/reflect/KClassifier;", - &[], - ) - .map_err(|err| zerror!(err))? - .l() - .map_err(|err| zerror!(err))?; - let classifier_obj = AutoLocal::new(classifier_obj, env); - - let kclass_class = env - .find_class("kotlin/reflect/KClass") - .map_err(|err| zerror!(err))?; - let is_kclass = env - .is_instance_of(&classifier_obj, kclass_class) - .map_err(|err| zerror!(err))?; - if is_kclass { - let qualified_name_jobject = env - .call_method( - &classifier_obj, - "getQualifiedName", - "()Ljava/lang/String;", - &[], - ) - .map_err(|err| zerror!(err))? - .l() - .map_err(|err| zerror!(err))?; - - let qualified_name: String = env - .get_string(&JString::from(qualified_name_jobject)) - .map_err(|err| zerror!(err))? - .into(); - - match qualified_name.as_str() { - "kotlin.Boolean" => Ok(KotlinType::Boolean), - "kotlin.String" => Ok(KotlinType::String), - "kotlin.ByteArray" => Ok(KotlinType::ByteArray), - "kotlin.Byte" => Ok(KotlinType::Byte), - "kotlin.Short" => Ok(KotlinType::Short), - "kotlin.Int" => Ok(KotlinType::Int), - "kotlin.Long" => Ok(KotlinType::Long), - "kotlin.Float" => Ok(KotlinType::Float), - "kotlin.Double" => Ok(KotlinType::Double), - "kotlin.UByte" => Ok(KotlinType::UByte), - "kotlin.UShort" => Ok(KotlinType::UShort), - "kotlin.UInt" => Ok(KotlinType::UInt), - "kotlin.ULong" => Ok(KotlinType::ULong), - "kotlin.collections.List" => Ok(KotlinType::List(Box::new(decode_ktype_arg( - env, &ktype, 0, - )?))), - "kotlin.collections.Map" => Ok(KotlinType::Map( - Box::new(decode_ktype_arg(env, &ktype, 0)?), - Box::new(decode_ktype_arg(env, &ktype, 1)?), - )), - "kotlin.Pair" => Ok(KotlinType::Pair( - Box::new(decode_ktype_arg(env, &ktype, 0)?), - Box::new(decode_ktype_arg(env, &ktype, 1)?), - )), - "kotlin.Triple" => Ok(KotlinType::Triple( - Box::new(decode_ktype_arg(env, &ktype, 0)?), - Box::new(decode_ktype_arg(env, &ktype, 1)?), - Box::new(decode_ktype_arg(env, &ktype, 2)?), - )), - _ => Err(zerror!("Unsupported type: {}", qualified_name)), - } - } else { - Err(zerror!("Classifier is not a KClass")) - } -} - -fn decode_ktype_arg(env: &mut JNIEnv, ktype: &JObject, idx: i32) -> ZResult { - let arguments = env - .call_method(ktype, "getArguments", "()Ljava/util/List;", &[]) - .map_err(|err| zerror!(err))? - .l() - .map_err(|err| zerror!(err))?; - let arg = env - .call_method( - &arguments, - "get", - "(I)Ljava/lang/Object;", - &[JValue::Int(idx)], - ) - .map_err(|err| zerror!(err))? - .l() - .map_err(|err| zerror!(err))?; - let ktype = env - .call_method(arg, "getType", "()Lkotlin/reflect/KType;", &[]) - .map_err(|err| zerror!(err))? - .l() - .map_err(|err| zerror!(err))?; - decode_ktype(env, ktype) -} - -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNIZBytes_serializeViaJNI( - mut env: JNIEnv, - _class: JClass, - any: JObject, - ktype: JObject, -) -> jobject { - || -> ZResult { - let mut serializer = ZSerializer::new(); - let ktype = decode_ktype(&mut env, ktype)?; - serialize(&mut env, &mut serializer, any, &ktype)?; - let zbytes = serializer.finish(); - - let byte_array = bytes_to_java_array(&env, &zbytes).map_err(|err| zerror!(err))?; - let zbytes_obj = env - .new_object( - "io/zenoh/bytes/ZBytes", - "([B)V", - &[JValue::Object(&JObject::from(byte_array))], - ) - .map_err(|err| zerror!(err))?; - - Ok(zbytes_obj.as_raw()) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - JObject::default().as_raw() - }) -} - -fn serialize( - env: &mut JNIEnv, - serializer: &mut ZSerializer, - any: JObject, - ktype: &KotlinType, -) -> ZResult<()> { - match ktype { - KotlinType::Byte => { - let byte_value = env - .call_method(any, "byteValue", "()B", &[]) - .map_err(|err| zerror!(err))? - .b() - .map_err(|err| zerror!(err))?; - serializer.serialize(byte_value); - } - KotlinType::Short => { - let short_value = env - .call_method(any, "shortValue", "()S", &[]) - .map_err(|err| zerror!(err))? - .s() - .map_err(|err| zerror!(err))?; - serializer.serialize(short_value); - } - KotlinType::Int => { - let int_value = env - .call_method(any, "intValue", "()I", &[]) - .map_err(|err| zerror!(err))? - .i() - .map_err(|err| zerror!(err))?; - serializer.serialize(int_value); - } - KotlinType::Long => { - let long_value = env - .call_method(any, "longValue", "()J", &[]) - .map_err(|err| zerror!(err))? - .j() - .map_err(|err| zerror!(err))?; - serializer.serialize(long_value); - } - KotlinType::Float => { - let float_value = env - .call_method(any, "floatValue", "()F", &[]) - .map_err(|err| zerror!(err))? - .f() - .map_err(|err| zerror!(err))?; - serializer.serialize(float_value); - } - KotlinType::Double => { - let double_value = env - .call_method(any, "doubleValue", "()D", &[]) - .map_err(|err| zerror!(err))? - .d() - .map_err(|err| zerror!(err))?; - serializer.serialize(double_value); - } - KotlinType::Boolean => { - let boolean_value = env - .call_method(any, "booleanValue", "()Z", &[]) - .map_err(|err| zerror!(err))? - .z() - .map_err(|err| zerror!(err))?; - serializer.serialize(boolean_value); - } - KotlinType::String => { - let jstring = JString::from(any); - let string_value: String = env.get_string(&jstring).map_err(|err| zerror!(err))?.into(); - serializer.serialize(string_value); - } - KotlinType::ByteArray => { - let jbyte_array = JByteArray::from(any); - let bytes = decode_byte_array(env, jbyte_array).map_err(|err| zerror!(err))?; - serializer.serialize(bytes); - } - KotlinType::UByte => { - let byte = env - .get_field(any, "data", "B") - .map_err(|err| zerror!(err))? - .b() - .map_err(|err| zerror!(err))?; - serializer.serialize(byte as u8); - } - KotlinType::UShort => { - let short = env - .get_field(any, "data", "S") - .map_err(|err| zerror!(err))? - .s() - .map_err(|err| zerror!(err))?; - serializer.serialize(short as u16); - } - KotlinType::UInt => { - let int = env - .get_field(any, "data", "I") - .map_err(|err| zerror!(err))? - .i() - .map_err(|err| zerror!(err))?; - serializer.serialize(int as u32); - } - KotlinType::ULong => { - let long = env - .get_field(any, "data", "J") - .map_err(|err| zerror!(err))? - .j() - .map_err(|err| zerror!(err))?; - serializer.serialize(long as u64); - } - KotlinType::List(kotlin_type) => { - let jlist: JList<'_, '_, '_> = - JList::from_env(env, &any).map_err(|err| zerror!(err))?; - let mut iterator = jlist.iter(env).map_err(|err| zerror!(err))?; - let list_size = jlist.size(env).unwrap(); - serializer.serialize(zenoh_ext::VarInt(list_size as usize)); - while let Some(value) = iterator.next(env).map_err(|err| zerror!(err))? { - serialize(env, serializer, value, kotlin_type)?; - } - } - KotlinType::Map(key_type, value_type) => { - let jmap = JMap::from_env(env, &any).map_err(|err| zerror!(err))?; - - let map_size = env - .call_method(&jmap, "size", "()I", &[]) - .map_err(|err| zerror!(err))? - .i() - .map_err(|err| zerror!(err))?; - - serializer.serialize(zenoh_ext::VarInt(map_size as usize)); - - let mut iterator = jmap.iter(env).map_err(|err| zerror!(err))?; - while let Some((key, value)) = iterator.next(env).map_err(|err| zerror!(err))? { - serialize(env, serializer, key, key_type)?; - serialize(env, serializer, value, value_type)?; - } - } - KotlinType::Pair(first_type, second_type) => { - let first = env - .call_method(&any, "getFirst", "()Ljava/lang/Object;", &[]) - .map_err(|err| zerror!(err))? - .l() - .map_err(|err| zerror!(err))?; - let second = env - .call_method(&any, "getSecond", "()Ljava/lang/Object;", &[]) - .map_err(|err| zerror!(err))? - .l() - .map_err(|err| zerror!(err))?; - serialize(env, serializer, first, first_type)?; - serialize(env, serializer, second, second_type)?; - } - KotlinType::Triple(first_type, second_type, third_type) => { - let first = env - .call_method(&any, "getFirst", "()Ljava/lang/Object;", &[]) - .map_err(|err| zerror!(err))? - .l() - .map_err(|err| zerror!(err))?; - let second = env - .call_method(&any, "getSecond", "()Ljava/lang/Object;", &[]) - .map_err(|err| zerror!(err))? - .l() - .map_err(|err| zerror!(err))?; - let third = env - .call_method(&any, "getThird", "()Ljava/lang/Object;", &[]) - .map_err(|err| zerror!(err))? - .l() - .map_err(|err| zerror!(err))?; - serialize(env, serializer, first, first_type)?; - serialize(env, serializer, second, second_type)?; - serialize(env, serializer, third, third_type)?; - } - } - Ok(()) -} - -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNIZBytes_deserializeViaJNI( - mut env: JNIEnv, - _class: JClass, - zbytes: JObject, - ktype: JObject, -) -> jobject { - || -> ZResult { - let payload = env - .get_field(zbytes, "bytes", "[B") - .map_err(|err| zerror!(err))?; - let decoded_bytes: Vec = - decode_byte_array(&env, JByteArray::from(payload.l().unwrap()))?; - let zbytes = ZBytes::from(decoded_bytes); - let mut deserializer = ZDeserializer::new(&zbytes); - let ktype = decode_ktype(&mut env, ktype)?; - let obj = deserialize(&mut env, &mut deserializer, &ktype)?; - if !deserializer.done() { - return Err(zerror!(ZDeserializeError)); - } - Ok(obj) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - JObject::default().as_raw() - }) -} - -fn deserialize( - env: &mut JNIEnv, - deserializer: &mut ZDeserializer, - ktype: &KotlinType, -) -> ZResult { - match ktype { - KotlinType::Byte => { - let byte = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let byte_obj = env - .new_object("java/lang/Byte", "(B)V", &[JValue::Byte(byte)]) - .map_err(|err| zerror!(err))?; - Ok(byte_obj.as_raw()) - } - KotlinType::Short => { - let short = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let short_obj = env - .new_object("java/lang/Short", "(S)V", &[JValue::Short(short)]) - .map_err(|err| zerror!(err))?; - Ok(short_obj.as_raw()) - } - KotlinType::Int => { - let integer = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let integer_obj = env - .new_object("java/lang/Integer", "(I)V", &[JValue::Int(integer)]) - .map_err(|err| zerror!(err))?; - Ok(integer_obj.as_raw()) - } - KotlinType::Long => { - let long = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let long_obj = env - .new_object("java/lang/Long", "(J)V", &[JValue::Long(long)]) - .map_err(|err| zerror!(err))?; - Ok(long_obj.as_raw()) - } - KotlinType::Float => { - let float = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let float_obj = env - .new_object("java/lang/Float", "(F)V", &[JValue::Float(float)]) - .map_err(|err| zerror!(err))?; - Ok(float_obj.as_raw()) - } - KotlinType::Double => { - let double = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let double_obj = env - .new_object("java/lang/Double", "(D)V", &[JValue::Double(double)]) - .map_err(|err| zerror!(err))?; - Ok(double_obj.as_raw()) - } - KotlinType::Boolean => { - let boolean_value = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let jboolean = if boolean_value { 1u8 } else { 0u8 }; - let boolean_obj = env - .new_object("java/lang/Boolean", "(Z)V", &[JValue::Bool(jboolean)]) - .map_err(|err| zerror!(err))?; - Ok(boolean_obj.as_raw()) - } - KotlinType::String => { - let deserialized_string = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let jstring = env - .new_string(&deserialized_string) - .map_err(|err| zerror!(err))?; - Ok(jstring.into_raw()) - } - KotlinType::ByteArray => { - let deserialized_bytes = deserializer - .deserialize::>() - .map_err(|err| zerror!(err))?; - let jbytes = env - .byte_array_from_slice(deserialized_bytes.as_slice()) - .map_err(|err| zerror!(err))?; - Ok(jbytes.into_raw()) - } - KotlinType::UByte => { - let ubyte = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let ubyte_obj = env - .new_object("kotlin/UByte", "(B)V", &[JValue::Byte(ubyte as i8)]) - .map_err(|err| zerror!(err))?; - Ok(ubyte_obj.as_raw()) - } - KotlinType::UShort => { - let ushort = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let ushort_obj = env - .new_object("kotlin/UShort", "(S)V", &[JValue::Short(ushort as i16)]) - .map_err(|err| zerror!(err))?; - Ok(ushort_obj.as_raw()) - } - KotlinType::UInt => { - let uint = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let uint_obj = env - .new_object("kotlin/UInt", "(I)V", &[JValue::Int(uint as i32)]) - .map_err(|err| zerror!(err))?; - Ok(uint_obj.as_raw()) - } - KotlinType::ULong => { - let ulong = deserializer - .deserialize::() - .map_err(|err| zerror!(err))?; - let ulong_obj = env - .new_object("kotlin/ULong", "(J)V", &[JValue::Long(ulong as i64)]) - .map_err(|err| zerror!(err))?; - Ok(ulong_obj.as_raw()) - } - KotlinType::List(kotlin_type) => { - let list_size = deserializer - .deserialize::>() - .map_err(|err| zerror!(err))? - .0; - let array_list = env - .new_object("java/util/ArrayList", "()V", &[]) - .map_err(|err| zerror!(err))?; - let jlist = JList::from_env(env, &array_list).map_err(|err| zerror!(err))?; - - for _ in 0..list_size { - let item = deserialize(env, deserializer, kotlin_type)?; - let item_obj = unsafe { JObject::from_raw(item) }; - jlist.add(env, &item_obj).map_err(|err| zerror!(err))?; - } - Ok(array_list.as_raw()) - } - KotlinType::Map(key_type, value_type) => { - let map_size = deserializer - .deserialize::>() - .map_err(|err| zerror!(err))? - .0; - let map = env - .new_object("java/util/HashMap", "()V", &[]) - .map_err(|err| zerror!(err))?; - let jmap = JMap::from_env(env, &map).map_err(|err| zerror!(err))?; - - for _ in 0..map_size { - let key = deserialize(env, deserializer, key_type)?; - let key_obj = unsafe { JObject::from_raw(key) }; - let value = deserialize(env, deserializer, value_type)?; - let value_obj = unsafe { JObject::from_raw(value) }; - jmap.put(env, &key_obj, &value_obj) - .map_err(|err| zerror!(err))?; - } - Ok(map.as_raw()) - } - KotlinType::Pair(first_type, second_type) => { - let first = deserialize(env, deserializer, first_type)?; - let second = deserialize(env, deserializer, second_type)?; - let pair = env - .new_object( - "kotlin/Pair", - "(Ljava/lang/Object;Ljava/lang/Object;)V", - &[ - JValue::Object(&unsafe { JObject::from_raw(first) }), - JValue::Object(&unsafe { JObject::from_raw(second) }), - ], - ) - .map_err(|err| zerror!(err))?; - Ok(pair.as_raw()) - } - KotlinType::Triple(first_type, second_type, third_type) => { - let first = deserialize(env, deserializer, first_type)?; - let second = deserialize(env, deserializer, second_type)?; - let third = deserialize(env, deserializer, third_type)?; - let triple = env - .new_object( - "kotlin/Triple", - "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V", - &[ - JValue::Object(&unsafe { JObject::from_raw(first) }), - JValue::Object(&unsafe { JObject::from_raw(second) }), - JValue::Object(&unsafe { JObject::from_raw(third) }), - ], - ) - .map_err(|err| zerror!(err))?; - Ok(triple.as_raw()) - } - } -} diff --git a/zenoh-jni/src/zenoh_id.rs b/zenoh-jni/src/zenoh_id.rs deleted file mode 100644 index 6647f86f..00000000 --- a/zenoh-jni/src/zenoh_id.rs +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use crate::{errors::ZResult, throw_exception, utils::decode_byte_array, zerror}; -use jni::{ - objects::{JByteArray, JClass, JString}, - sys::jstring, - JNIEnv, -}; -use zenoh::session::ZenohId; - -/// Returns the string representation of a ZenohID. -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn Java_io_zenoh_jni_JNIZenohId_toStringViaJNI( - mut env: JNIEnv, - _class: JClass, - zenoh_id: JByteArray, -) -> jstring { - || -> ZResult { - let bytes = decode_byte_array(&env, zenoh_id)?; - let zenohid = ZenohId::try_from(bytes.as_slice()).map_err(|err| zerror!(err))?; - env.new_string(zenohid.to_string()) - .map_err(|err| zerror!(err)) - }() - .unwrap_or_else(|err| { - throw_exception!(env, err); - JString::default() - }) - .as_raw() -} diff --git a/zenoh-kotlin/build.gradle.kts b/zenoh-kotlin/build.gradle.kts index f17c3e73..6c5e766b 100644 --- a/zenoh-kotlin/build.gradle.kts +++ b/zenoh-kotlin/build.gradle.kts @@ -12,8 +12,6 @@ // ZettaScale Zenoh Team, // -import com.nishtahir.CargoExtension - plugins { kotlin("multiplatform") kotlin("plugin.serialization") @@ -24,19 +22,13 @@ plugins { } val androidEnabled = project.findProperty("android")?.toString()?.toBoolean() == true -val release = project.findProperty("release")?.toString()?.toBoolean() == true // If the publication is meant to be done on a remote repository (Maven central). // Modifying this property will affect the release workflows! val isRemotePublication = project.findProperty("remotePublication")?.toString()?.toBoolean() == true -var buildMode = if (release) BuildMode.RELEASE else BuildMode.DEBUG - if (androidEnabled) { apply(plugin = "com.android.library") - apply(plugin = "org.mozilla.rust-android-gradle.rust-android") - - configureCargo() configureAndroid() } @@ -60,7 +52,7 @@ kotlin { implementation("commons-net:commons-net:3.9.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") - implementation("org.jetbrains.kotlin:kotlin-reflect") + implementation("org.eclipse.zenoh:zenoh-jni-runtime:${property("zenohJniRuntimeVersion")}") } } val commonTest by getting { @@ -71,25 +63,21 @@ kotlin { implementation("org.junit.jupiter:junit-jupiter-params:5.10.0") } } + // jvmAndAndroidMain is an intermediate source set between commonMain and both jvmMain/androidMain. + // It holds code that uses kotlin-reflect — available on JVM and Android (ART), + // but absent on Kotlin/Native and Kotlin/JS targets. + val jvmAndAndroidMain by creating { dependsOn(commonMain) } + val jvmMain by getting { + dependsOn(jvmAndAndroidMain) + } if (androidEnabled) { + val androidMain by getting { dependsOn(jvmAndAndroidMain) } val androidUnitTest by getting { dependencies { implementation(kotlin("test-junit")) } } } - val jvmMain by getting { - if (isRemotePublication) { - // The line below is intended to load the native libraries that are crosscompiled on GitHub actions when publishing a JVM package. - resources.srcDir("../jni-libs").include("*/**") - } else { - resources.srcDir("../zenoh-jni/target/$buildMode").include(arrayListOf("*.dylib", "*.so", "*.dll")) - } - } - - val jvmTest by getting { - resources.srcDir("../zenoh-jni/target/$buildMode").include(arrayListOf("*.dylib", "*.so", "*.dll")) - } } val javadocJar by tasks.registering(Jar::class) { @@ -164,65 +152,6 @@ tasks.withType().configureEach { dependsOn(tasks.withType()) } -tasks.withType { - doFirst { - // The line below is added for the Android Unit tests which are equivalent to the JVM tests. - // For them to work we need to specify the path to the native library as a system property and not as a jvmArg. - systemProperty("java.library.path", "../zenoh-jni/target/$buildMode") - } -} - -tasks.whenObjectAdded { - if ((this.name == "mergeDebugJniLibFolders" || this.name == "mergeReleaseJniLibFolders")) { - this.dependsOn("cargoBuild") - } -} - -tasks.named("compileKotlinJvm") { - dependsOn("buildZenohJni") -} - -tasks.register("buildZenohJni") { - doLast { - if (!isRemotePublication) { - // This is intended for local publications. For publications done through GitHub workflows, - // the zenoh-jni build is achieved and loaded differently from the CI - buildZenohJNI(buildMode) - } - } -} - -fun buildZenohJNI(mode: BuildMode = BuildMode.DEBUG) { - val cargoCommand = mutableListOf("cargo", "build") - - if (mode == BuildMode.RELEASE) { - cargoCommand.add("--release") - } - - val result = project.exec { - commandLine(*(cargoCommand.toTypedArray()), "--manifest-path", "../zenoh-jni/Cargo.toml") - } - - if (result.exitValue != 0) { - throw GradleException("Failed to build Zenoh-JNI.") - } - - Thread.sleep(1000) -} - -enum class BuildMode { - DEBUG { - override fun toString(): String { - return "debug" - } - }, - RELEASE { - override fun toString(): String { - return "release" - } - } -} - fun Project.configureAndroid() { extensions.configure("android") { namespace = "io.zenoh" @@ -260,20 +189,3 @@ fun Project.configureAndroid() { } } } - -fun Project.configureCargo() { - extensions.configure("cargo") { - pythonCommand = "python3" - module = "../zenoh-jni" - libname = "zenoh-jni" - targetIncludes = arrayOf("libzenoh_jni.so") - targetDirectory = "../zenoh-jni/target/" - profile = "release" - targets = arrayListOf( - "arm", - "arm64", - "x86", - "x86_64", - ) - } -} diff --git a/zenoh-kotlin/src/androidMain/kotlin/io.zenoh/Zenoh.kt b/zenoh-kotlin/src/androidMain/kotlin/io.zenoh/Zenoh.kt deleted file mode 100644 index 74d42e51..00000000 --- a/zenoh-kotlin/src/androidMain/kotlin/io.zenoh/Zenoh.kt +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh - -/** - * Static singleton class to load the Zenoh native library once and only once, as well as the logger in function of the - * log level configuration. - */ -internal actual object ZenohLoad { - private const val ZENOH_LIB_NAME = "zenoh_jni" - - init { - System.loadLibrary(ZENOH_LIB_NAME) - } -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Config.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Config.kt index a6f69f44..1a653c37 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Config.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Config.kt @@ -132,7 +132,7 @@ class Config internal constructor(internal val jniConfig: JNIConfig) { * Returns the default config. */ fun default(): Config { - return JNIConfig.loadDefaultConfig() + return Config(JNIConfig.loadDefault()) } /** @@ -143,7 +143,7 @@ class Config internal constructor(internal val jniConfig: JNIConfig) { * @return A result with the [Config]. */ fun fromFile(file: File): Result { - return JNIConfig.loadConfigFile(file) + return runCatching { Config(JNIConfig.loadFromFile(file.toPath().toString())) } } /** @@ -154,7 +154,7 @@ class Config internal constructor(internal val jniConfig: JNIConfig) { * @return A result with the [Config]. */ fun fromFile(path: Path): Result { - return JNIConfig.loadConfigFile(path) + return runCatching { Config(JNIConfig.loadFromFile(path.toString())) } } /** @@ -190,7 +190,7 @@ class Config internal constructor(internal val jniConfig: JNIConfig) { * @return A result with the [Config]. */ fun fromJson(config: String): Result { - return JNIConfig.loadJsonConfig(config) + return runCatching { Config(JNIConfig.loadFromJson(config)) } } /** @@ -226,7 +226,7 @@ class Config internal constructor(internal val jniConfig: JNIConfig) { * @return A result with the [Config]. */ fun fromJson5(config: String): Result { - return JNIConfig.loadJson5Config(config) + return runCatching { Config(JNIConfig.loadFromJson(config)) } } /** @@ -258,7 +258,7 @@ class Config internal constructor(internal val jniConfig: JNIConfig) { * @return A result with the [Config]. */ fun fromYaml(config: String): Result { - return JNIConfig.loadYamlConfig(config) + return runCatching { Config(JNIConfig.loadFromYaml(config)) } } /** @@ -267,7 +267,7 @@ class Config internal constructor(internal val jniConfig: JNIConfig) { * @param jsonElement The zenoh config as a [JsonElement]. */ fun fromJsonElement(jsonElement: JsonElement): Result { - return JNIConfig.loadJsonConfig(jsonElement.toString()) + return runCatching { Config(JNIConfig.loadFromJson(jsonElement.toString())) } } /** @@ -289,7 +289,7 @@ class Config internal constructor(internal val jniConfig: JNIConfig) { * Returns the json value associated to the [key]. */ fun getJson(key: String): Result { - return jniConfig.getJson(key) + return runCatching { jniConfig.getJson(key) } } /** @@ -313,7 +313,7 @@ class Config internal constructor(internal val jniConfig: JNIConfig) { * @return A result with the status of the operation. */ fun insertJson5(key: String, value: String): Result { - return jniConfig.insertJson5(key, value) + return runCatching { jniConfig.insertJson5(key, value) } } protected fun finalize() { diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Logger.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Logger.kt index 540e004c..86aa4845 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Logger.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Logger.kt @@ -14,7 +14,7 @@ package io.zenoh -import io.zenoh.exceptions.ZError +import io.zenoh.jni.JNILogger /** Logger class to redirect the Rust logs from Zenoh to the kotlin environment. */ internal class Logger { @@ -24,16 +24,7 @@ internal class Logger { internal const val LOG_ENV: String = "RUST_LOG" fun start(filter: String) = runCatching { - startLogsViaJNI(filter) + JNILogger.startLogs(filter) } - - /** - * Redirects the rust logs either to logcat for Android systems or to the standard output (for non-android - * systems). - * - * See https://docs.rs/env_logger/latest/env_logger/index.html for accepted filter format. - */ - @Throws(ZError::class) - private external fun startLogsViaJNI(filter: String) } } diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Session.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Session.kt index c3ad5ccc..11db22fb 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Session.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Session.kt @@ -15,13 +15,23 @@ package io.zenoh import io.zenoh.annotations.Unstable +import io.zenoh.config.EntityGlobalId import io.zenoh.exceptions.ZError +import io.zenoh.ext.HeartbeatMode +import io.zenoh.ext.RecoveryMode import io.zenoh.handlers.Callback import io.zenoh.handlers.ChannelHandler import io.zenoh.handlers.Handler +import io.zenoh.jni.JNIQuery import io.zenoh.jni.JNISession +import io.zenoh.jni.callbacks.JNIGetCallback +import io.zenoh.jni.callbacks.JNIOnCloseCallback +import io.zenoh.jni.callbacks.JNIQueryableCallback +import io.zenoh.jni.callbacks.JNISubscriberCallback import io.zenoh.keyexpr.KeyExpr import io.zenoh.bytes.Encoding +import io.zenoh.qos.CongestionControl +import io.zenoh.qos.Priority import io.zenoh.qos.QoS import io.zenoh.bytes.IntoZBytes import io.zenoh.bytes.ZBytes @@ -39,7 +49,9 @@ import io.zenoh.pubsub.Put import io.zenoh.query.* import io.zenoh.query.Query import io.zenoh.query.Queryable +import io.zenoh.query.ReplyError import io.zenoh.sample.Sample +import io.zenoh.sample.SampleKind import io.zenoh.query.Selector import io.zenoh.qos.Reliability import io.zenoh.session.SessionDeclaration @@ -48,6 +60,7 @@ import io.zenoh.pubsub.Subscriber import kotlinx.coroutines.channels.Channel import java.lang.ref.WeakReference import java.time.Duration +import org.apache.commons.net.ntp.TimeStamp /** * A Zenoh Session, the core interaction point with a Zenoh network. @@ -654,7 +667,8 @@ class Session private constructor(private val config: Config) : AutoCloseable { */ fun declareKeyExpr(keyExpr: String): Result { return jniSession?.run { - declareKeyExpr(keyExpr).onSuccess { strongDeclarations.add(it) } + runCatching { KeyExpr(keyExpr, declareKeyExpr(keyExpr)) } + .onSuccess { strongDeclarations.add(it) } } ?: Result.failure(sessionClosedException) } @@ -669,7 +683,12 @@ class Session private constructor(private val config: Config) : AutoCloseable { */ fun undeclare(keyExpr: KeyExpr): Result { return jniSession?.run { - undeclareKeyExpr(keyExpr) + val jniKeyExpr = keyExpr.jniKeyExpr + ?: return Result.failure(ZError("Key expression is not declared through a session.")) + runCatching { + undeclareKeyExpr(jniKeyExpr) + keyExpr.jniKeyExpr = null + } } ?: Result.failure(sessionClosedException) } @@ -1076,7 +1095,12 @@ class Session private constructor(private val config: Config) : AutoCloseable { reliability: Reliability ): Result { return jniSession?.run { - declarePublisher(keyExpr, qos, encoding, reliability).onSuccess { weakDeclarations.add(WeakReference(it)) } + runCatching { + Publisher(keyExpr, qos, encoding, declarePublisher( + keyExpr.jniKeyExpr, keyExpr.keyExpr, + qos.congestionControl.value, qos.priority.value, qos.express, reliability.ordinal + )) + }.onSuccess { weakDeclarations.add(WeakReference(it)) } } ?: Result.failure(sessionClosedException) } @@ -1091,8 +1115,27 @@ class Session private constructor(private val config: Config) : AutoCloseable { publisherDetection: Boolean = false ): Result { return jniSession?.run { - declareAdvancedPublisher(keyExpr, qos, encoding, reliability, - cacheConfig, sampleMissDetection, publisherDetection).onSuccess { weakDeclarations.add(WeakReference(it)) } + runCatching { + val cacheEnabled = cacheConfig != null + val cacheMaxSamples = cacheConfig?.maxSamples ?: 1L + val cacheRepliesQoS = cacheConfig?.repliesQoS ?: QoS.defaultPush + val smdEnabled = sampleMissDetection != null + val heartbeat = sampleMissDetection?.heartbeat + val smdEnableHeartbeat = heartbeat != null + val heartbeatMs = when (heartbeat) { + is HeartbeatMode.PeriodicHeartbeat -> heartbeat.milliseconds + is HeartbeatMode.SporadicHeartbeat -> heartbeat.milliseconds + else -> 0L + } + val heartbeatIsSporadic = heartbeat is HeartbeatMode.SporadicHeartbeat + AdvancedPublisher(keyExpr, qos, encoding, declareAdvancedPublisher( + keyExpr.jniKeyExpr, keyExpr.keyExpr, + qos.congestionControl.value, qos.priority.value, qos.express, reliability.ordinal, + cacheEnabled, cacheMaxSamples, + cacheRepliesQoS.priority.value, cacheRepliesQoS.congestionControl.value, cacheRepliesQoS.express, + smdEnabled, smdEnableHeartbeat, heartbeatMs, heartbeatIsSporadic, publisherDetection + )) + }.onSuccess { weakDeclarations.add(WeakReference(it)) } } ?: Result.failure(sessionClosedException) } @@ -1103,7 +1146,20 @@ class Session private constructor(private val config: Config) : AutoCloseable { receiver: R ): Result> { return jniSession?.run { - declareSubscriber(keyExpr, callback, onClose, receiver).onSuccess { strongDeclarations.add(it) } + runCatching { + val jniCallback = JNISubscriberCallback { keyExpr1, payload, encodingId, encodingSchema, kind, timestampNTP64, timestampIsValid, attachmentBytes, express, priority, congestionControl -> + val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null + callback.run(Sample( + KeyExpr(keyExpr1), ZBytes.from(payload), Encoding(encodingId, schema = encodingSchema), + SampleKind.fromInt(kind), timestamp, + QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), + attachmentBytes?.let { ZBytes.from(it) } + )) + } + Subscriber(keyExpr, receiver, declareSubscriber( + keyExpr.jniKeyExpr, keyExpr.keyExpr, jniCallback, JNIOnCloseCallback { onClose() } + )) + }.onSuccess { strongDeclarations.add(it) } } ?: Result.failure(sessionClosedException) } @@ -1118,7 +1174,30 @@ class Session private constructor(private val config: Config) : AutoCloseable { receiver: R ): Result> { return jniSession?.run { - declareAdvancedSubscriber(keyExpr, history, recovery, subscriberDetection, callback, onClose, receiver).onSuccess { strongDeclarations.add(it) } + runCatching { + val jniCallback = JNISubscriberCallback { keyExpr1, payload, encodingId, encodingSchema, kind, timestampNTP64, timestampIsValid, attachmentBytes, express, priority, congestionControl -> + val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null + callback.run(Sample( + KeyExpr(keyExpr1), ZBytes.from(payload), Encoding(encodingId, schema = encodingSchema), + SampleKind.fromInt(kind), timestamp, + QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), + attachmentBytes?.let { ZBytes.from(it) } + )) + } + val historyEnabled = history != null + val historyDetectLate = history?.detectLatePublishers ?: false + val historyMaxSamples = history?.maxSamples ?: 0L + val historyMaxAgeSecs = history?.maxAgeSeconds ?: 0.0 + val recoveryEnabled = recovery != null + val isHeartbeat = recovery?.mode is RecoveryMode.Heartbeat + val recoveryQueryPeriodMs = (recovery?.mode as? RecoveryMode.PeriodicQuery)?.milliseconds ?: 0L + AdvancedSubscriber(keyExpr, receiver, declareAdvancedSubscriber( + keyExpr.jniKeyExpr, keyExpr.keyExpr, + historyEnabled, historyDetectLate, historyMaxSamples, historyMaxAgeSecs, + recoveryEnabled, isHeartbeat, recoveryQueryPeriodMs, + subscriberDetection, jniCallback, JNIOnCloseCallback { onClose() } + )) + }.onSuccess { strongDeclarations.add(it) } } ?: Result.failure(sessionClosedException) } @@ -1130,7 +1209,25 @@ class Session private constructor(private val config: Config) : AutoCloseable { complete: Boolean ): Result> { return jniSession?.run { - declareQueryable(keyExpr, callback, onClose, receiver, complete).onSuccess { strongDeclarations.add(it) } + runCatching { + val jniCallback = JNIQueryableCallback { keyExpr1, selectorParams, payload, encodingId, encodingSchema, attachmentBytes, queryPtr, acceptReplies -> + val jniQuery = JNIQuery(queryPtr) + val keyExpr2 = KeyExpr(keyExpr1) + val selector = if (selectorParams.isEmpty()) Selector(keyExpr2) + else Selector(keyExpr2, Parameters.from(selectorParams).getOrThrow()) + callback.run(Query( + keyExpr2, selector, + payload?.let { ZBytes.from(it) }, + payload?.let { Encoding(encodingId, schema = encodingSchema) }, + attachmentBytes?.let { ZBytes.from(it) }, + jniQuery, + ReplyKeyExpr.entries[acceptReplies] + )) + } + Queryable(keyExpr, receiver, declareQueryable( + keyExpr.jniKeyExpr, keyExpr.keyExpr, jniCallback, JNIOnCloseCallback { onClose() }, complete + )) + }.onSuccess { strongDeclarations.add(it) } } ?: Result.failure(sessionClosedException) } @@ -1143,7 +1240,14 @@ class Session private constructor(private val config: Config) : AutoCloseable { acceptReplies: ReplyKeyExpr ): Result { return jniSession?.run { - declareQuerier(keyExpr, target, consolidation, qos, timeout, acceptReplies).onSuccess { weakDeclarations.add(WeakReference(it)) } + runCatching { + Querier(keyExpr, qos, declareQuerier( + keyExpr.jniKeyExpr, keyExpr.keyExpr, + target.ordinal, consolidation.ordinal, + qos.congestionControl.value, qos.priority.value, qos.express, + timeout.toMillis(), acceptReplies.ordinal + )) + }.onSuccess { weakDeclarations.add(WeakReference(it)) } } ?: Result.failure(sessionClosedException) } @@ -1162,48 +1266,78 @@ class Session private constructor(private val config: Config) : AutoCloseable { acceptReplies: ReplyKeyExpr ): Result { return jniSession?.run { - performGet( - selector, - callback, - onClose, - receiver, - timeout, - target, - consolidation, - payload, - encoding, - attachment, - qos, - acceptReplies - ) + runCatching { + val jniCallback = JNIGetCallback { replierZid, replierEid, success, replyKeyExpr, replyPayload, encodingId, encodingSchema, kind, timestampNTP64, timestampIsValid, replyAttachment, express, priority, congestionControl -> + val reply = if (success) { + val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null + Reply( + replierZid?.let { EntityGlobalId(ZenohId(it), replierEid.toUInt()) }, + Result.success(Sample( + KeyExpr(replyKeyExpr!!), ZBytes.from(replyPayload), + Encoding(encodingId, schema = encodingSchema), + SampleKind.fromInt(kind), timestamp, + QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), + replyAttachment?.let { ZBytes.from(it) } + )) + ) + } else { + Reply( + replierZid?.let { EntityGlobalId(ZenohId(it), replierEid.toUInt()) }, + Result.failure(ReplyError(ZBytes.from(replyPayload), Encoding(encodingId, schema = encodingSchema))) + ) + } + callback.run(reply) + } + val resolvedEncoding = encoding ?: Encoding.default() + get( + selector.keyExpr.jniKeyExpr, selector.keyExpr.keyExpr, selector.parameters?.toString(), + jniCallback, JNIOnCloseCallback { onClose() }, + timeout.toMillis(), target.ordinal, consolidation.ordinal, + attachment?.into()?.bytes, payload?.into()?.bytes, + resolvedEncoding.id, resolvedEncoding.schema, + qos.congestionControl.value, qos.priority.value, qos.express, acceptReplies.ordinal + ) + receiver + } } ?: Result.failure(sessionClosedException) } private fun resolvePut(keyExpr: KeyExpr, put: Put): Result = runCatching { - jniSession?.run { performPut(keyExpr, put) } + jniSession?.run { + put( + keyExpr.jniKeyExpr, keyExpr.keyExpr, + put.payload.bytes, put.encoding.id, put.encoding.schema, + put.qos.congestionControl.value, put.qos.priority.value, put.qos.express, + put.attachment?.bytes, put.reliability.ordinal + ) + } } private fun resolveDelete(keyExpr: KeyExpr, delete: Delete): Result = runCatching { - jniSession?.run { performDelete(keyExpr, delete) } + jniSession?.run { + delete( + keyExpr.jniKeyExpr, keyExpr.keyExpr, + delete.qos.congestionControl.value, delete.qos.priority.value, delete.qos.express, + delete.attachment?.bytes, delete.reliability.ordinal + ) + } } internal fun zid(): Result { - return jniSession?.zid() ?: Result.failure(sessionClosedException) + return jniSession?.run { runCatching { ZenohId(getZid()) } } ?: Result.failure(sessionClosedException) } internal fun getPeersId(): Result> { - return jniSession?.peersZid() ?: Result.failure(sessionClosedException) + return jniSession?.run { runCatching { getPeersZid().map { ZenohId(it) } } } ?: Result.failure(sessionClosedException) } internal fun getRoutersId(): Result> { - return jniSession?.routersZid() ?: Result.failure(sessionClosedException) + return jniSession?.run { runCatching { getRoutersZid().map { ZenohId(it) } } } ?: Result.failure(sessionClosedException) } /** Launches the session through the jni session, returning the [Session] on success. */ private fun launch(): Result = runCatching { - jniSession = JNISession() - return jniSession!!.open(config) - .map { this@Session } - .onFailure { jniSession = null } + jniSession = JNISession.open(config.jniConfig) + this@Session } } diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Zenoh.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Zenoh.kt index 12049903..b45409fd 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Zenoh.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/Zenoh.kt @@ -15,14 +15,17 @@ package io.zenoh import io.zenoh.Logger.Companion.LOG_ENV +import io.zenoh.config.WhatAmI +import io.zenoh.config.WhatAmI.* +import io.zenoh.config.ZenohId import io.zenoh.handlers.Callback import io.zenoh.handlers.ChannelHandler import io.zenoh.handlers.Handler import io.zenoh.jni.JNIScout +import io.zenoh.jni.callbacks.JNIOnCloseCallback +import io.zenoh.jni.callbacks.JNIScoutCallback import io.zenoh.scouting.Hello import io.zenoh.scouting.Scout -import io.zenoh.config.WhatAmI -import io.zenoh.config.WhatAmI.* import kotlinx.coroutines.channels.Channel object Zenoh { @@ -54,7 +57,13 @@ object Zenoh { config: Config? = null ): Result> { ZenohLoad - return JNIScout.scout(whatAmI = whatAmI, callback = callback, receiver = Unit, onClose = {}, config = config) + return runCatching { + val binaryWhatAmI = whatAmI.map { it.value }.reduce { acc, it -> acc or it } + val jniCallback = JNIScoutCallback { whatAmI2, zid, locators -> + callback.run(Hello(WhatAmI.fromInt(whatAmI2), ZenohId(zid), locators)) + } + Scout(Unit, JNIScout.scout(binaryWhatAmI, jniCallback, JNIOnCloseCallback {}, config?.jniConfig)) + } } /** @@ -74,13 +83,13 @@ object Zenoh { config: Config? = null ): Result> { ZenohLoad - return JNIScout.scout( - whatAmI = whatAmI, - callback = { hello -> handler.handle(hello) }, - receiver = handler.receiver(), - onClose = handler::onClose, - config = config - ) + return runCatching { + val binaryWhatAmI = whatAmI.map { it.value }.reduce { acc, it -> acc or it } + val jniCallback = JNIScoutCallback { whatAmI2, zid, locators -> + handler.handle(Hello(WhatAmI.fromInt(whatAmI2), ZenohId(zid), locators)) + } + Scout(handler.receiver(), JNIScout.scout(binaryWhatAmI, jniCallback, JNIOnCloseCallback { handler.onClose() }, config?.jniConfig)) + } } /** @@ -101,13 +110,13 @@ object Zenoh { ): Result>> { ZenohLoad val handler = ChannelHandler(channel) - return JNIScout.scout( - whatAmI = whatAmI, - callback = { hello -> handler.handle(hello) }, - receiver = handler.receiver(), - onClose = handler::onClose, - config = config - ) + return runCatching { + val binaryWhatAmI = whatAmI.map { it.value }.reduce { acc, it -> acc or it } + val jniCallback = JNIScoutCallback { whatAmI2, zid, locators -> + handler.handle(Hello(WhatAmI.fromInt(whatAmI2), ZenohId(zid), locators)) + } + Scout(handler.receiver(), JNIScout.scout(binaryWhatAmI, jniCallback, JNIOnCloseCallback { handler.onClose() }, config?.jniConfig)) + } } /** @@ -143,9 +152,3 @@ object Zenoh { logLevelProp?.let { Logger.start(it) } ?: Logger.start(fallbackFilter) } } - -/** - * Static singleton class to load the Zenoh native library once and only once, as well as the logger in function of the - * log level configuration. - */ -internal expect object ZenohLoad diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/config/ZenohId.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/config/ZenohId.kt index d9848514..e60f9078 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/config/ZenohId.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/config/ZenohId.kt @@ -22,7 +22,7 @@ import io.zenoh.jni.JNIZenohId data class ZenohId internal constructor(internal val bytes: ByteArray) { override fun toString(): String { - return JNIZenohId.toStringViaJNI(bytes) + return JNIZenohId.toString(bytes) } override fun equals(other: Any?): Boolean { diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/exceptions/ZError.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/exceptions/ZError.kt deleted file mode 100644 index c63a19ce..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/exceptions/ZError.kt +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.exceptions - -/** - * A Zenoh Error. - */ -class ZError(override val message: String? = null): Exception() diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIAdvancedPublisher.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIAdvancedPublisher.kt deleted file mode 100644 index b629f6d0..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIAdvancedPublisher.kt +++ /dev/null @@ -1,124 +0,0 @@ -// -// Copyright (c) 2025 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.exceptions.ZError -import io.zenoh.bytes.Encoding -import io.zenoh.bytes.IntoZBytes -import io.zenoh.handlers.MatchingCallback -import io.zenoh.jni.callbacks.JNIMatchingListenerCallback -import io.zenoh.jni.callbacks.JNIOnCloseCallback -import io.zenoh.pubsub.AdvancedPublisher -import io.zenoh.pubsub.MatchingListener - -/** - * Adapter class to handle the interactions with Zenoh through JNI for an [AdvancedPublisher]. - * - * @property ptr: raw pointer to the underlying native Publisher. - */ -internal class JNIAdvancedPublisher(private val ptr: Long) { - - fun declareMatchingListener( - callback: MatchingCallback, onClose: () -> Unit - ): Result = runCatching { - val matchingListenerCallback = - JNIMatchingListenerCallback { matching: Boolean -> - callback.run(matching) - } - val matchingListenerRawPtr = declareMatchingListenerViaJNI( - ptr, matchingListenerCallback, onClose - ) - MatchingListener(JNIMatchingListener(matchingListenerRawPtr)) - } - - fun declareBackgroundMatchingListener( - callback: MatchingCallback, onClose: () -> Unit - ): Result = runCatching { - val matchingListenerCallback = - JNIMatchingListenerCallback { matching: Boolean -> - callback.run(matching) - } - declareBackgroundMatchingListenerViaJNI( - ptr, matchingListenerCallback, onClose - ) - } - - /** - * Return the matching status of the publisher. - * - * Will return true if there exist Subscribers matching the Publisher's key expression and false otherwise. - */ - fun getMatchingStatus(): Result = runCatching { - getMatchingStatusViaJNI(ptr) - } - - /** - * Put operation. - * - * @param payload Payload of the put. - * @param encoding Encoding of the payload. - * @param attachment Optional attachment. - */ - fun put(payload: IntoZBytes, encoding: Encoding?, attachment: IntoZBytes?): Result = runCatching { - val resolvedEncoding = encoding ?: Encoding.default() - putViaJNI(payload.into().bytes, resolvedEncoding.id, resolvedEncoding.schema, attachment?.into()?.bytes, ptr) - } - - /** - * Delete operation. - * - * @param attachment Optional attachment. - */ - fun delete(attachment: IntoZBytes?): Result = runCatching { - deleteViaJNI(attachment?.into()?.bytes, ptr) - } - - /** - * Close and free the underlying publisher pointer. - * - * Further operations with this publisher should not be performed anymore. - */ - fun close() { - freePtrViaJNI(ptr) - } - - @Throws(ZError::class) - private external fun declareMatchingListenerViaJNI( - ptr: Long, - callback: JNIMatchingListenerCallback, - onClose: JNIOnCloseCallback, - ): Long - - @Throws(ZError::class) - private external fun declareBackgroundMatchingListenerViaJNI( - ptr: Long, - callback: JNIMatchingListenerCallback, - onClose: JNIOnCloseCallback, - ) - - @Throws(ZError::class) - private external fun getMatchingStatusViaJNI(ptr: Long): Boolean - - @Throws(ZError::class) - private external fun putViaJNI( - valuePayload: ByteArray, encodingId: Int, encodingSchema: String?, attachment: ByteArray?, ptr: Long - ) - - @Throws(ZError::class) - private external fun deleteViaJNI(attachment: ByteArray?, ptr: Long) - - private external fun freePtrViaJNI(ptr: Long) - -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIAdvancedSubscriber.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIAdvancedSubscriber.kt deleted file mode 100644 index 99a396e5..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIAdvancedSubscriber.kt +++ /dev/null @@ -1,168 +0,0 @@ -// -// Copyright (c) 2025 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.bytes.Encoding -import io.zenoh.bytes.into -import io.zenoh.exceptions.ZError -import io.zenoh.handlers.Callback -import io.zenoh.handlers.MatchingCallback -import io.zenoh.handlers.SampleMissCallback -import io.zenoh.jni.callbacks.JNIMatchingListenerCallback -import io.zenoh.jni.callbacks.JNIOnCloseCallback -import io.zenoh.jni.callbacks.JNISampleMissListenerCallback -import io.zenoh.jni.callbacks.JNISubscriberCallback -import io.zenoh.keyexpr.KeyExpr -import io.zenoh.pubsub.AdvancedSubscriber -import io.zenoh.pubsub.MatchingListener -import io.zenoh.pubsub.SampleMiss -import io.zenoh.pubsub.SampleMissListener -import io.zenoh.pubsub.Subscriber -import io.zenoh.qos.CongestionControl -import io.zenoh.qos.Priority -import io.zenoh.qos.QoS -import io.zenoh.sample.Sample -import io.zenoh.sample.SampleKind -import org.apache.commons.net.ntp.TimeStamp - -/** - * Adapter class to handle the interactions with Zenoh through JNI for an [AdvancedSubscriber] - * - * @property ptr: raw pointer to the underlying native Subscriber. - */ -internal class JNIAdvancedSubscriber(private val ptr: Long) { - - fun declareDetectPublishersSubscriber( - keyExpr: KeyExpr, - history: Boolean, - callback: Callback, - onClose: () -> Unit, receiver: R - ): Result> = runCatching { - val subCallback = - JNISubscriberCallback { keyExpr, payload, encodingId, encodingSchema, kind, timestampNTP64, timestampIsValid, attachmentBytes, express: Boolean, priority: Int, congestionControl: Int -> - val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null - val sample = Sample( - KeyExpr(keyExpr, null), - payload.into(), - Encoding(encodingId, schema = encodingSchema), - SampleKind.fromInt(kind), - timestamp, - QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), - attachmentBytes?.into() - ) - callback.run(sample) - } - val subscriberRawPtr = declareDetectPublishersSubscriberViaJNI( - ptr, history, subCallback, onClose - ) - Subscriber(keyExpr, receiver, JNISubscriber(subscriberRawPtr)) - } - - fun declareBackgroundDetectPublishersSubscriber( - keyExpr: KeyExpr, - history: Boolean, - callback: Callback, - onClose: () -> Unit - ): Result = runCatching { - val subCallback = - JNISubscriberCallback { keyExpr, payload, encodingId, encodingSchema, kind, timestampNTP64, timestampIsValid, attachmentBytes, express: Boolean, priority: Int, congestionControl: Int -> - val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null - val sample = Sample( - KeyExpr(keyExpr, null), - payload.into(), - Encoding(encodingId, schema = encodingSchema), - SampleKind.fromInt(kind), - timestamp, - QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), - attachmentBytes?.into() - ) - callback.run(sample) - } - declareBackgroundDetectPublishersSubscriberViaJNI( - ptr, history, subCallback, onClose - ) - } - - fun declareSampleMissListener( - callback: SampleMissCallback, onClose: () -> Unit - ): Result = runCatching { - val sampleMissListenerCallback = - JNISampleMissListenerCallback { zidLower: Long, - zidUpper: Long, - eid: Long, - missedCount: Long -> - val miss = SampleMiss(zidLower, zidUpper, eid, missedCount) - callback.run(miss) - } - val sampleMissListenerRawPtr = declareSampleMissListenerViaJNI( - ptr, sampleMissListenerCallback, onClose - ) - SampleMissListener(JNISampleMissListener(sampleMissListenerRawPtr)) - } - - fun declareBackgroundSampleMissListener( - callback: SampleMissCallback, onClose: () -> Unit - ): Result = runCatching { - val sampleMissListenerCallback = - JNISampleMissListenerCallback { zidLower: Long, - zidUpper: Long, - eid: Long, - missedCount: Long -> - val miss = SampleMiss(zidLower, zidUpper, eid, missedCount) - callback.run(miss) - } - declareBackgroundSampleMissListenerViaJNI( - ptr, sampleMissListenerCallback, onClose - ) - } - - fun close() { - freePtrViaJNI(ptr) - } - - @Throws(ZError::class) - private external fun declareBackgroundDetectPublishersSubscriberViaJNI( - advancedSubscriberPtr: Long, - history: Boolean, - callback: JNISubscriberCallback, - onClose: JNIOnCloseCallback, - ) - - @Throws(ZError::class) - private external fun declareDetectPublishersSubscriberViaJNI( - advancedSubscriberPtr: Long, - history: Boolean, - callback: JNISubscriberCallback, - onClose: JNIOnCloseCallback, - ): Long - - @Throws(ZError::class) - private external fun declareBackgroundSampleMissListenerViaJNI( - ptr: Long, - callback: JNISampleMissListenerCallback, - onClose: JNIOnCloseCallback, - ) - - @Throws(ZError::class) - private external fun declareSampleMissListenerViaJNI( - ptr: Long, - callback: JNISampleMissListenerCallback, - onClose: JNIOnCloseCallback, - ): Long - - /** Frees the underlying native Subscriber. */ - private external fun freePtrViaJNI(ptr: Long) - -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIConfig.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIConfig.kt deleted file mode 100644 index 4c7c0344..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIConfig.kt +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.Config -import io.zenoh.ZenohLoad -import io.zenoh.exceptions.ZError -import java.io.File -import java.nio.file.Path - -internal class JNIConfig(internal val ptr: Long) { - - companion object { - - init { - ZenohLoad - } - - fun loadDefaultConfig(): Config { - val cfgPtr = loadDefaultConfigViaJNI() - return Config(JNIConfig(cfgPtr)) - } - - fun loadConfigFile(path: Path): Result = runCatching { - val cfgPtr = loadConfigFileViaJNI(path.toString()) - Config(JNIConfig(cfgPtr)) - } - - fun loadConfigFile(file: File): Result = loadConfigFile(file.toPath()) - - fun loadJsonConfig(rawConfig: String): Result = runCatching { - val cfgPtr = loadJsonConfigViaJNI(rawConfig) - Config(JNIConfig(cfgPtr)) - } - - fun loadJson5Config(rawConfig: String): Result = runCatching { - val cfgPtr = loadJsonConfigViaJNI(rawConfig) - Config(JNIConfig(cfgPtr)) - } - - fun loadYamlConfig(rawConfig: String): Result = runCatching { - val cfgPtr = loadYamlConfigViaJNI(rawConfig) - Config(JNIConfig(cfgPtr)) - } - - @Throws(ZError::class) - private external fun loadDefaultConfigViaJNI(): Long - - @Throws(ZError::class) - private external fun loadConfigFileViaJNI(path: String): Long - - @Throws(ZError::class) - private external fun loadJsonConfigViaJNI(rawConfig: String): Long - - @Throws(ZError::class) - private external fun loadYamlConfigViaJNI(rawConfig: String): Long - - @Throws(ZError::class) - private external fun getIdViaJNI(ptr: Long): ByteArray - - @Throws(ZError::class) - private external fun insertJson5ViaJNI(ptr: Long, key: String, value: String): Long - - /** Frees the underlying native config. */ - private external fun freePtrViaJNI(ptr: Long) - - @Throws(ZError::class) - private external fun getJsonViaJNI(ptr: Long, key: String): String - } - - fun close() { - freePtrViaJNI(ptr) - } - - fun getJson(key: String): Result = runCatching { - getJsonViaJNI(ptr, key) - } - - fun insertJson5(key: String, value: String): Result = runCatching { - insertJson5ViaJNI(this.ptr, key, value) - } -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt deleted file mode 100644 index 7022e521..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt +++ /dev/null @@ -1,97 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.ZenohLoad -import io.zenoh.exceptions.ZError -import io.zenoh.keyexpr.KeyExpr -import io.zenoh.keyexpr.SetIntersectionLevel - -internal class JNIKeyExpr(internal val ptr: Long) { - - companion object { - init { - ZenohLoad - } - - fun tryFrom(keyExpr: String): Result = runCatching { - KeyExpr(tryFromViaJNI(keyExpr)) - } - - fun autocanonize(keyExpr: String): Result = runCatching { - KeyExpr(autocanonizeViaJNI(keyExpr)) - } - - fun intersects(keyExprA: KeyExpr, keyExprB: KeyExpr): Boolean = intersectsViaJNI( - keyExprA.jniKeyExpr?.ptr ?: 0, - keyExprA.keyExpr, - keyExprB.jniKeyExpr?.ptr ?: 0, - keyExprB.keyExpr - ) - - fun includes(keyExprA: KeyExpr, keyExprB: KeyExpr): Boolean = includesViaJNI( - keyExprA.jniKeyExpr?.ptr ?: 0, - keyExprA.keyExpr, - keyExprB.jniKeyExpr?.ptr ?: 0, - keyExprB.keyExpr - ) - - fun relationTo(keyExpr: KeyExpr, other: KeyExpr): SetIntersectionLevel { - val intersection = relationToViaJNI( - keyExpr.jniKeyExpr?.ptr ?: 0, - keyExpr.keyExpr, - other.jniKeyExpr?.ptr ?: 0, - other.keyExpr - ) - return SetIntersectionLevel.fromInt(intersection) - } - - fun joinViaJNI(keyExpr: KeyExpr, other: String): Result = runCatching { - KeyExpr(joinViaJNI(keyExpr.jniKeyExpr?.ptr ?: 0, keyExpr.keyExpr, other)) - } - - fun concatViaJNI(keyExpr: KeyExpr, other: String): Result = runCatching { - KeyExpr(concatViaJNI(keyExpr.jniKeyExpr?.ptr ?: 0, keyExpr.keyExpr, other)) - } - - @Throws(ZError::class) - private external fun tryFromViaJNI(keyExpr: String): String - - @Throws(ZError::class) - private external fun autocanonizeViaJNI(keyExpr: String): String - - @Throws(ZError::class) - private external fun intersectsViaJNI(ptrA: Long, keyExprA: String, ptrB: Long, keyExprB: String): Boolean - - @Throws(ZError::class) - private external fun includesViaJNI(ptrA: Long, keyExprA: String, ptrB: Long, keyExprB: String): Boolean - - @Throws(ZError::class) - private external fun relationToViaJNI(ptrA: Long, keyExprA: String, ptrB: Long, keyExprB: String): Int - - @Throws(ZError::class) - private external fun joinViaJNI(ptrA: Long, keyExprA: String, other: String): String - - @Throws(ZError::class) - private external fun concatViaJNI(ptrA: Long, keyExprA: String, other: String): String - } - - fun close() { - freePtrViaJNI(ptr) - } - - /** Frees the underlying native KeyExpr. */ - private external fun freePtrViaJNI(ptr: Long) -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNILiveliness.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNILiveliness.kt index 9434e257..2914d5bb 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNILiveliness.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNILiveliness.kt @@ -15,7 +15,7 @@ package io.zenoh.jni import io.zenoh.bytes.Encoding -import io.zenoh.bytes.into +import io.zenoh.bytes.ZBytes import io.zenoh.config.EntityGlobalId import io.zenoh.config.ZenohId import io.zenoh.handlers.Callback @@ -49,14 +49,14 @@ internal object JNILiveliness { replierZid: ByteArray?, replierEid: Int, success: Boolean, - keyExpr2: String?, - payload: ByteArray, + replyKeyExpr: String?, + replyPayload: ByteArray, encodingId: Int, encodingSchema: String?, kind: Int, timestampNTP64: Long, timestampIsValid: Boolean, - attachmentBytes: ByteArray?, + replyAttachment: ByteArray?, express: Boolean, priority: Int, congestionControl: Int, @@ -65,20 +65,20 @@ internal object JNILiveliness { if (success) { val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null val sample = Sample( - KeyExpr(keyExpr2!!, null), - payload.into(), + KeyExpr(replyKeyExpr!!, null), + ZBytes.from(replyPayload), Encoding(encodingId, schema = encodingSchema), SampleKind.fromInt(kind), timestamp, QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), - attachmentBytes?.into() + replyAttachment?.let { ZBytes.from(it) } ) reply = Reply(replierZid?.let { EntityGlobalId(ZenohId(it), replierEid.toUInt()) }, Result.success(sample)) } else { reply = Reply( replierZid?.let { EntityGlobalId(ZenohId(it), replierEid.toUInt()) }, Result.failure( ReplyError( - payload.into(), + ZBytes.from(replyPayload), Encoding(encodingId, schema = encodingSchema) ) ) @@ -86,20 +86,18 @@ internal object JNILiveliness { } callback.run(reply) } - getViaJNI( - jniSession.sessionPtr.get(), - keyExpr.jniKeyExpr?.ptr ?: 0, + jniSession.livelinessGet( + keyExpr.jniKeyExpr, keyExpr.keyExpr, getCallback, timeout.toMillis(), - onClose + JNIOnCloseCallback { onClose() } ) receiver } fun declareToken(jniSession: JNISession, keyExpr: KeyExpr): LivelinessToken { - val ptr = declareTokenViaJNI(jniSession.sessionPtr.get(), keyExpr.jniKeyExpr?.ptr ?: 0, keyExpr.keyExpr) - return LivelinessToken(JNILivelinessToken(ptr)) + return LivelinessToken(jniSession.declareLivelinessToken(keyExpr.jniKeyExpr, keyExpr.keyExpr)) } fun declareSubscriber( @@ -115,43 +113,22 @@ internal object JNILiveliness { val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null val sample = Sample( KeyExpr(keyExpr2, null), - payload.into(), + ZBytes.from(payload), Encoding(encodingId, schema = encodingSchema), SampleKind.fromInt(kind), timestamp, QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), - attachmentBytes?.into() + attachmentBytes?.let { ZBytes.from(it) } ) callback.run(sample) } - val ptr = declareSubscriberViaJNI( - jniSession.sessionPtr.get(), - keyExpr.jniKeyExpr?.ptr ?: 0, + val jniSubscriber = jniSession.declareLivelinessSubscriber( + keyExpr.jniKeyExpr, keyExpr.keyExpr, subCallback, history, - onClose + JNIOnCloseCallback { onClose() } ) - Subscriber(keyExpr, receiver, JNISubscriber(ptr)) + Subscriber(keyExpr, receiver, jniSubscriber) } - - private external fun getViaJNI( - sessionPtr: Long, - keyExprPtr: Long, - keyExprString: String, - callback: JNIGetCallback, - timeoutMs: Long, - onClose: JNIOnCloseCallback - ) - - private external fun declareTokenViaJNI(sessionPtr: Long, keyExprPtr: Long, keyExprString: String): Long - - private external fun declareSubscriberViaJNI( - sessionPtr: Long, - keyExprPtr: Long, - keyExprString: String, - callback: JNISubscriberCallback, - history: Boolean, - onClose: JNIOnCloseCallback - ): Long } diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNILivelinessToken.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNILivelinessToken.kt deleted file mode 100644 index 991c860a..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNILivelinessToken.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.zenoh.jni - -internal class JNILivelinessToken(val ptr: Long) { - - fun undeclare() { - undeclareViaJNI(this.ptr) - } - - companion object { - external fun undeclareViaJNI(ptr: Long) - } -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIMatchingListener.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIMatchingListener.kt deleted file mode 100644 index c2895378..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIMatchingListener.kt +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright (c) 2025 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.pubsub.MatchingListener - -/** - * Adapter class to handle the interactions with Zenoh through JNI for a [MatchingListener]. - * - * @property ptr: raw pointer to the underlying native Publisher. - */ -internal class JNIMatchingListener(private val ptr: Long) { - - /** - * Close and free the underlying matching listener pointer. - * - * Further operations with this publisher should not be performed anymore. - */ - fun close() { - freePtrViaJNI(ptr) - } - - private external fun freePtrViaJNI(ptr: Long) - -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIPublisher.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIPublisher.kt deleted file mode 100644 index a5c3ea9c..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIPublisher.kt +++ /dev/null @@ -1,69 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.exceptions.ZError -import io.zenoh.bytes.Encoding -import io.zenoh.bytes.IntoZBytes -import io.zenoh.pubsub.Publisher - -/** - * Adapter class to handle the interactions with Zenoh through JNI for a [Publisher]. - * - * @property ptr: raw pointer to the underlying native Publisher. - */ -internal class JNIPublisher(private val ptr: Long) { - - /** - * Put operation. - * - * @param payload Payload of the put. - * @param encoding Encoding of the payload. - * @param attachment Optional attachment. - */ - fun put(payload: IntoZBytes, encoding: Encoding?, attachment: IntoZBytes?): Result = runCatching { - val resolvedEncoding = encoding ?: Encoding.default() - putViaJNI(payload.into().bytes, resolvedEncoding.id, resolvedEncoding.schema, attachment?.into()?.bytes, ptr) - } - - /** - * Delete operation. - * - * @param attachment Optional attachment. - */ - fun delete(attachment: IntoZBytes?): Result = runCatching { - deleteViaJNI(attachment?.into()?.bytes, ptr) - } - - /** - * Close and free the underlying publisher pointer. - * - * Further operations with this publisher should not be performed anymore. - */ - fun close() { - freePtrViaJNI(ptr) - } - - @Throws(ZError::class) - private external fun putViaJNI( - valuePayload: ByteArray, encodingId: Int, encodingSchema: String?, attachment: ByteArray?, ptr: Long - ) - - @Throws(ZError::class) - private external fun deleteViaJNI(attachment: ByteArray?, ptr: Long) - - private external fun freePtrViaJNI(ptr: Long) - -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQuerier.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQuerier.kt deleted file mode 100644 index 28a195e0..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQuerier.kt +++ /dev/null @@ -1,123 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.bytes.Encoding -import io.zenoh.bytes.IntoZBytes -import io.zenoh.bytes.into -import io.zenoh.config.EntityGlobalId -import io.zenoh.config.ZenohId -import io.zenoh.exceptions.ZError -import io.zenoh.handlers.Callback -import io.zenoh.jni.callbacks.JNIGetCallback -import io.zenoh.jni.callbacks.JNIOnCloseCallback -import io.zenoh.keyexpr.KeyExpr -import io.zenoh.qos.CongestionControl -import io.zenoh.qos.Priority -import io.zenoh.qos.QoS -import io.zenoh.query.Parameters -import io.zenoh.query.Reply -import io.zenoh.query.ReplyError -import io.zenoh.sample.Sample -import io.zenoh.sample.SampleKind -import org.apache.commons.net.ntp.TimeStamp - -internal class JNIQuerier(val ptr: Long) { - - fun performGet( - keyExpr: KeyExpr, - parameters: Parameters?, - callback: Callback, - onClose: () -> Unit, - receiver: R, - attachment: IntoZBytes?, - payload: IntoZBytes?, - encoding: Encoding? - ): Result = runCatching { - val getCallback = JNIGetCallback { - replierZid: ByteArray?, - replierEid: Int, - success: Boolean, - keyExpr: String?, - payload: ByteArray, - encodingId: Int, - encodingSchema: String?, - kind: Int, - timestampNTP64: Long, - timestampIsValid: Boolean, - attachmentBytes: ByteArray?, - express: Boolean, - priority: Int, - congestionControl: Int, - -> - val reply: Reply - if (success) { - val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null - val sample = Sample( - KeyExpr(keyExpr!!, null), - payload.into(), - Encoding(encodingId, schema = encodingSchema), - SampleKind.fromInt(kind), - timestamp, - QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), - attachmentBytes?.into() - ) - reply = Reply(replierZid?.let { EntityGlobalId(ZenohId(it), replierEid.toUInt()) }, Result.success(sample)) - } else { - reply = Reply( - replierZid?.let { EntityGlobalId(ZenohId(it), replierEid.toUInt()) }, Result.failure( - ReplyError( - payload.into(), - Encoding(encodingId, schema = encodingSchema) - ) - ) - ) - } - callback.run(reply) - } - getViaJNI(this.ptr, - keyExpr.jniKeyExpr?.ptr ?: 0, - keyExpr.keyExpr, - parameters?.toString(), - getCallback, - onClose, - attachment?.into()?.bytes, - payload?.into()?.bytes, - encoding?.id ?: Encoding.default().id, - encoding?.schema - ) - receiver - } - - fun close() { - freePtrViaJNI(ptr) - } - - @Throws(ZError::class) - private external fun getViaJNI( - querierPtr: Long, - keyExprPtr: Long, - keyExprString: String, - parameters: String?, - callback: JNIGetCallback, - onClose: JNIOnCloseCallback, - attachmentBytes: ByteArray?, - payload: ByteArray?, - encodingId: Int, - encodingSchema: String?, - ) - - private external fun freePtrViaJNI(ptr: Long) -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQuery.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQuery.kt deleted file mode 100644 index b176b970..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQuery.kt +++ /dev/null @@ -1,107 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.exceptions.ZError -import io.zenoh.keyexpr.KeyExpr -import io.zenoh.bytes.Encoding -import io.zenoh.qos.QoS -import io.zenoh.bytes.IntoZBytes -import io.zenoh.sample.Sample -import org.apache.commons.net.ntp.TimeStamp - -/** - * Adapter class for interacting with a Query using JNI. - * - * This class serves as an adapter for interacting with a Query through JNI (Java Native Interface). - * - * @property ptr The raw pointer to the underlying native query. - */ -internal class JNIQuery(private val ptr: Long) { - - fun replySuccess(sample: Sample): Result = runCatching { - val timestampEnabled = sample.timestamp != null - replySuccessViaJNI( - ptr, - sample.keyExpr.jniKeyExpr?.ptr ?: 0, - sample.keyExpr.keyExpr, - sample.payload.bytes, - sample.encoding.id, - sample.encoding.schema, - timestampEnabled, - if (timestampEnabled) sample.timestamp!!.ntpValue() else 0, - sample.attachment?.bytes, - sample.qos.express, - ) - } - - fun replyError(error: IntoZBytes, encoding: Encoding): Result = runCatching { - replyErrorViaJNI(ptr, error.into().bytes, encoding.id, encoding.schema) - } - - fun replyDelete(keyExpr: KeyExpr, timestamp: TimeStamp?, attachment: IntoZBytes?, qos: QoS): Result = - runCatching { - val timestampEnabled = timestamp != null - replyDeleteViaJNI( - ptr, - keyExpr.jniKeyExpr?.ptr ?: 0, - keyExpr.keyExpr, - timestampEnabled, - if (timestampEnabled) timestamp!!.ntpValue() else 0, - attachment?.into()?.bytes, - qos.express, - ) - } - - fun close() { - freePtrViaJNI(ptr) - } - - @Throws(ZError::class) - private external fun replySuccessViaJNI( - queryPtr: Long, - keyExprPtr: Long, - keyExprString: String, - valuePayload: ByteArray, - valueEncodingId: Int, - valueEncodingSchema: String?, - timestampEnabled: Boolean, - timestampNtp64: Long, - attachment: ByteArray?, - qosExpress: Boolean, - ) - - @Throws(ZError::class) - private external fun replyErrorViaJNI( - queryPtr: Long, - errorValuePayload: ByteArray, - errorValueEncoding: Int, - encodingSchema: String?, - ) - - @Throws(ZError::class) - private external fun replyDeleteViaJNI( - queryPtr: Long, - keyExprPtr: Long, - keyExprString: String, - timestampEnabled: Boolean, - timestampNtp64: Long, - attachment: ByteArray?, - qosExpress: Boolean, - ) - - /** Frees the underlying native Query. */ - private external fun freePtrViaJNI(ptr: Long) -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQueryable.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQueryable.kt deleted file mode 100644 index e5f7d3ce..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIQueryable.kt +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -/** - * Adapter class to handle the interactions with Zenoh through JNI for a [io.zenoh.query.Queryable] - * - * @property ptr: raw pointer to the underlying native Queryable. - */ -internal class JNIQueryable(val ptr: Long) { - - fun close() { - freePtrViaJNI(ptr) - } - - /** Frees the underlying native Queryable. */ - private external fun freePtrViaJNI(ptr: Long) -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISampleMissListener.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISampleMissListener.kt deleted file mode 100644 index b7f5b5ed..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISampleMissListener.kt +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright (c) 2025 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.pubsub.SampleMissListener - -/** - * Adapter class to handle the interactions with Zenoh through JNI for a [SampleMissListener]. - * - * @property ptr: raw pointer to the underlying native Publisher. - */ -internal class JNISampleMissListener(private val ptr: Long) { - - /** - * Close and free the underlying matching listener pointer. - * - * Further operations with this publisher should not be performed anymore. - */ - fun close() { - freePtrViaJNI(ptr) - } - - private external fun freePtrViaJNI(ptr: Long) - -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIScout.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIScout.kt deleted file mode 100644 index cdc3f5e2..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIScout.kt +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.Config -import io.zenoh.exceptions.ZError -import io.zenoh.handlers.Callback -import io.zenoh.jni.callbacks.JNIScoutCallback -import io.zenoh.config.ZenohId -import io.zenoh.scouting.Hello -import io.zenoh.scouting.Scout -import io.zenoh.config.WhatAmI -import io.zenoh.jni.callbacks.JNIOnCloseCallback - -/** - * Adapter class to handle the interactions with Zenoh through JNI for a [io.zenoh.scouting.Scout] - * - * @property ptr: raw pointer to the underlying native scout. - */ -internal class JNIScout(private val ptr: Long) { - - companion object { - fun scout( - whatAmI: Set, - callback: Callback, - onClose: () -> Unit, - config: Config?, - receiver: R - ): Result> = runCatching { - val scoutCallback = JNIScoutCallback { whatAmI2: Int, id: ByteArray, locators: List -> - callback.run(Hello(WhatAmI.fromInt(whatAmI2), ZenohId(id), locators)) - } - val binaryWhatAmI: Int = whatAmI.map { it.value }.reduce { acc, it -> acc or it } - val ptr = scoutViaJNI(binaryWhatAmI, scoutCallback, onClose,config?.jniConfig?.ptr ?: 0) - Scout(receiver, JNIScout(ptr)) - } - - @Throws(ZError::class) - private external fun scoutViaJNI( - whatAmI: Int, - callback: JNIScoutCallback, - onCloseCallback: JNIOnCloseCallback, - configPtr: Long, - ): Long - - @Throws(ZError::class) - external fun freePtrViaJNI(ptr: Long) - } - - fun close() { - freePtrViaJNI(ptr) - } -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt deleted file mode 100644 index 214abe06..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt +++ /dev/null @@ -1,552 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.* -import io.zenoh.bytes.Encoding -import io.zenoh.exceptions.ZError -import io.zenoh.handlers.Callback -import io.zenoh.jni.callbacks.JNIOnCloseCallback -import io.zenoh.jni.callbacks.JNIGetCallback -import io.zenoh.jni.callbacks.JNIQueryableCallback -import io.zenoh.jni.callbacks.JNISubscriberCallback -import io.zenoh.keyexpr.KeyExpr -import io.zenoh.bytes.IntoZBytes -import io.zenoh.config.ZenohId -import io.zenoh.bytes.into -import io.zenoh.Config -import io.zenoh.config.EntityGlobalId -import io.zenoh.pubsub.AdvancedSubscriber -import io.zenoh.pubsub.AdvancedPublisher -import io.zenoh.pubsub.Delete -import io.zenoh.pubsub.Publisher -import io.zenoh.pubsub.Put -import io.zenoh.pubsub.Subscriber -import io.zenoh.qos.CongestionControl -import io.zenoh.qos.Priority -import io.zenoh.qos.QoS -import io.zenoh.query.* -import io.zenoh.query.Query -import io.zenoh.query.Queryable -import io.zenoh.sample.Sample -import io.zenoh.query.Parameters -import io.zenoh.query.Selector -import io.zenoh.qos.Reliability -import io.zenoh.sample.SampleKind -import io.zenoh.ext.CacheConfig -import io.zenoh.ext.HeartbeatMode -import io.zenoh.ext.MissDetectionConfig -import io.zenoh.ext.HistoryConfig -import io.zenoh.ext.RecoveryConfig -import io.zenoh.ext.RecoveryMode -import org.apache.commons.net.ntp.TimeStamp -import java.time.Duration -import java.util.concurrent.atomic.AtomicLong - -/** Adapter class to handle the communication with the Zenoh JNI code for a [Session]. */ -internal class JNISession { - - companion object { - init { - ZenohLoad - } - } - - /* Pointer to the underlying Rust zenoh session. */ - internal var sessionPtr: AtomicLong = AtomicLong(0) - - fun open(config: Config): Result = runCatching { - val session = openSessionViaJNI(config.jniConfig.ptr) - sessionPtr.set(session) - } - - fun close(): Result = runCatching { - closeSessionViaJNI(sessionPtr.get()) - } - - fun declarePublisher(keyExpr: KeyExpr, qos: QoS, encoding: Encoding, reliability: Reliability): Result = runCatching { - val publisherRawPtr = declarePublisherViaJNI( - keyExpr.jniKeyExpr?.ptr ?: 0, - keyExpr.keyExpr, - sessionPtr.get(), - qos.congestionControl.value, - qos.priority.value, - qos.express, - reliability.ordinal - ) - Publisher( - keyExpr, - qos, - encoding, - JNIPublisher(publisherRawPtr), - ) - } - - fun declareAdvancedPublisher( - keyExpr: KeyExpr, - qos: QoS, - encoding: Encoding, - reliability: Reliability, - cache: CacheConfig?, - sampleMissDetection: MissDetectionConfig?, - publisherDetection: Boolean - ): Result = runCatching { - - val publisherRawPtr = declareAdvancedPublisherViaJNI( - keyExpr.jniKeyExpr?.ptr ?: 0, - keyExpr.keyExpr, - sessionPtr.get(), - qos.congestionControl.value, - qos.priority.value, - qos.express, - reliability.ordinal, - cache != null, - cache?.maxSamples ?: 0, - cache?.repliesQoS?.priority?.value ?: 0, - cache?.repliesQoS?.congestionControl?.value ?: 0, - cache?.repliesQoS?.express ?: false, - sampleMissDetection != null, - sampleMissDetection?.heartbeat != null, - when(val heartbeat = sampleMissDetection?.heartbeat) { - is HeartbeatMode.PeriodicHeartbeat -> heartbeat.milliseconds - is HeartbeatMode.SporadicHeartbeat -> heartbeat.milliseconds - null -> 0 - }, - when(sampleMissDetection?.heartbeat) { - is HeartbeatMode.PeriodicHeartbeat -> false - is HeartbeatMode.SporadicHeartbeat -> true - null -> false - }, - publisherDetection - ) - AdvancedPublisher( - keyExpr, - qos, - encoding, - JNIAdvancedPublisher(publisherRawPtr), - ) - } - - fun declareSubscriber( - keyExpr: KeyExpr, callback: Callback, onClose: () -> Unit, receiver: R - ): Result> = runCatching { - val subCallback = - JNISubscriberCallback { keyExpr, payload, encodingId, encodingSchema, kind, timestampNTP64, timestampIsValid, attachmentBytes, express: Boolean, priority: Int, congestionControl: Int -> - val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null - val sample = Sample( - KeyExpr(keyExpr, null), - payload.into(), - Encoding(encodingId, schema = encodingSchema), - SampleKind.fromInt(kind), - timestamp, - QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), - attachmentBytes?.into() - ) - callback.run(sample) - } - val subscriberRawPtr = declareSubscriberViaJNI( - keyExpr.jniKeyExpr?.ptr ?: 0, keyExpr.keyExpr, sessionPtr.get(), subCallback, onClose - ) - Subscriber(keyExpr, receiver, JNISubscriber(subscriberRawPtr)) - } - - fun declareAdvancedSubscriber( - keyExpr: KeyExpr, - history: HistoryConfig?, - recovery: RecoveryConfig?, - subscriberDetection: Boolean, - callback: Callback, - onClose: () -> Unit, - receiver: R - ): Result> = runCatching { - val subCallback = - JNISubscriberCallback { keyExpr, payload, encodingId, encodingSchema, kind, timestampNTP64, timestampIsValid, attachmentBytes, express: Boolean, priority: Int, congestionControl: Int -> - val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null - val sample = Sample( - KeyExpr(keyExpr, null), - payload.into(), - Encoding(encodingId, schema = encodingSchema), - SampleKind.fromInt(kind), - timestamp, - QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), - attachmentBytes?.into() - ) - callback.run(sample) - } - val subscriberRawPtr = declareAdvancedSubscriberViaJNI( - keyExpr.jniKeyExpr?.ptr ?: 0, - keyExpr.keyExpr, - sessionPtr.get(), - history != null, - history?.detectLatePublishers ?: false, - history?.maxSamples ?: 0, - history?.maxAgeSeconds ?: 0.0, - recovery != null, - when(recovery?.mode){ - is RecoveryMode.Heartbeat -> true - is RecoveryMode.PeriodicQuery -> false - null -> false - }, - when(val mode = recovery?.mode){ - is RecoveryMode.Heartbeat -> 0 - is RecoveryMode.PeriodicQuery -> mode.milliseconds - null -> 0 - }, - subscriberDetection, - subCallback, - onClose - ) - AdvancedSubscriber(keyExpr, receiver, JNIAdvancedSubscriber(subscriberRawPtr)) - } - - fun declareQueryable( - keyExpr: KeyExpr, callback: Callback, onClose: () -> Unit, receiver: R, complete: Boolean - ): Result> = runCatching { - val queryCallback = - JNIQueryableCallback { keyExpr: String, selectorParams: String, payload: ByteArray?, encodingId: Int, encodingSchema: String?, attachmentBytes: ByteArray?, queryPtr: Long -> - val jniQuery = JNIQuery(queryPtr) - val keyExpr2 = KeyExpr(keyExpr, null) - val selector = if (selectorParams.isEmpty()) { - Selector(keyExpr2) - } else { - Selector(keyExpr2, Parameters.from(selectorParams).getOrThrow()) - } - val query = Query( - keyExpr2, - selector, - payload?.into(), - payload?.let { Encoding(encodingId, schema = encodingSchema) }, - attachmentBytes?.into(), - jniQuery - ) - callback.run(query) - } - val queryableRawPtr = declareQueryableViaJNI( - keyExpr.jniKeyExpr?.ptr ?: 0, keyExpr.keyExpr, sessionPtr.get(), queryCallback, onClose, complete - ) - Queryable(keyExpr, receiver, JNIQueryable(queryableRawPtr)) - } - - fun declareQuerier( - keyExpr: KeyExpr, - target: QueryTarget, - consolidation: ConsolidationMode, - qos: QoS, - timeout: Duration, - acceptReplies: ReplyKeyExpr - ): Result = runCatching { - val querierRawPtr = declareQuerierViaJNI( - keyExpr.jniKeyExpr?.ptr ?: 0, keyExpr.keyExpr, sessionPtr.get(), target.ordinal, consolidation.ordinal, - qos.congestionControl.value, qos.priority.value, qos.express, timeout.toMillis(), acceptReplies.ordinal - ) - Querier(keyExpr, qos, JNIQuerier(querierRawPtr)) - } - - fun performGet( - selector: Selector, - callback: Callback, - onClose: () -> Unit, - receiver: R, - timeout: Duration, - target: QueryTarget, - consolidation: ConsolidationMode, - payload: IntoZBytes?, - encoding: Encoding?, - attachment: IntoZBytes?, - qos: QoS, - acceptReplies: ReplyKeyExpr - ): Result = runCatching { - val getCallback = JNIGetCallback { - replierZid: ByteArray?, - replierEid: Int, - success: Boolean, - keyExpr: String?, - payload: ByteArray, - encodingId: Int, - encodingSchema: String?, - kind: Int, - timestampNTP64: Long, - timestampIsValid: Boolean, - attachmentBytes: ByteArray?, - express: Boolean, - priority: Int, - congestionControl: Int, - -> - val reply: Reply - if (success) { - val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null - val sample = Sample( - KeyExpr(keyExpr!!, null), - payload.into(), - Encoding(encodingId, schema = encodingSchema), - SampleKind.fromInt(kind), - timestamp, - QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), - attachmentBytes?.into() - ) - reply = Reply(replierZid?.let { EntityGlobalId(ZenohId(it), replierEid.toUInt()) }, Result.success(sample)) - } else { - reply = Reply( - replierZid?.let { EntityGlobalId(ZenohId(it), replierEid.toUInt()) }, Result.failure( - ReplyError( - payload.into(), - Encoding(encodingId, schema = encodingSchema) - ) - ) - ) - } - callback.run(reply) - } - - getViaJNI( - selector.keyExpr.jniKeyExpr?.ptr ?: 0, - selector.keyExpr.keyExpr, - selector.parameters?.toString(), - sessionPtr.get(), - getCallback, - onClose, - timeout.toMillis(), - target.ordinal, - consolidation.ordinal, - attachment?.into()?.bytes, - payload?.into()?.bytes, - encoding?.id ?: Encoding.default().id, - encoding?.schema, - qos.congestionControl.value, - qos.priority.value, - qos.express, - acceptReplies.ordinal, - ) - receiver - } - - fun declareKeyExpr(keyExpr: String): Result = runCatching { - val ptr = declareKeyExprViaJNI(sessionPtr.get(), keyExpr) - return Result.success(KeyExpr(keyExpr, JNIKeyExpr(ptr))) - } - - fun undeclareKeyExpr(keyExpr: KeyExpr): Result = runCatching { - keyExpr.jniKeyExpr?.run { - undeclareKeyExprViaJNI(sessionPtr.get(), this.ptr) - keyExpr.jniKeyExpr = null - } ?: throw ZError("Attempting to undeclare a non declared key expression.") - } - - @Throws(ZError::class) - fun performPut( - keyExpr: KeyExpr, - put: Put, - ) { - putViaJNI( - keyExpr.jniKeyExpr?.ptr ?: 0, - keyExpr.keyExpr, - sessionPtr.get(), - put.payload.bytes, - put.encoding.id, - put.encoding.schema, - put.qos.congestionControl.value, - put.qos.priority.value, - put.qos.express, - put.attachment?.bytes, - put.reliability.ordinal - ) - } - - @Throws(ZError::class) - fun performDelete( - keyExpr: KeyExpr, - delete: Delete, - ) { - deleteViaJNI( - keyExpr.jniKeyExpr?.ptr ?: 0, - keyExpr.keyExpr, - sessionPtr.get(), - delete.qos.congestionControl.value, - delete.qos.priority.value, - delete.qos.express, - delete.attachment?.bytes, - delete.reliability.ordinal - ) - } - - fun zid(): Result = runCatching { - ZenohId(getZidViaJNI(sessionPtr.get())) - } - - fun peersZid(): Result> = runCatching { - getPeersZidViaJNI(sessionPtr.get()).map { ZenohId(it) } - } - - fun routersZid(): Result> = runCatching { - getRoutersZidViaJNI(sessionPtr.get()).map { ZenohId(it) } - } - - @Throws(ZError::class) - private external fun getZidViaJNI(ptr: Long): ByteArray - - @Throws(ZError::class) - private external fun getPeersZidViaJNI(ptr: Long): List - - @Throws(ZError::class) - private external fun getRoutersZidViaJNI(ptr: Long): List - - @Throws(ZError::class) - private external fun openSessionViaJNI(configPtr: Long): Long - - @Throws(ZError::class) - private external fun closeSessionViaJNI(ptr: Long) - - @Throws(ZError::class) - private external fun declarePublisherViaJNI( - keyExprPtr: Long, - keyExprString: String, - sessionPtr: Long, - congestionControl: Int, - priority: Int, - express: Boolean, - reliability: Int - ): Long - - @Throws(ZError::class) - private external fun declareAdvancedPublisherViaJNI( - keyExprPtr: Long, - keyExprString: String, - sessionPtr: Long, - congestionControl: Int, - priority: Int, - express: Boolean, - reliability: Int, - // CacheConfig - cacheEnabled: Boolean, - cacheMaxSamples: Long, - cacheRepliesPriority: Int, - cacheRepliesCongestionControl: Int, - cacheRepliesIsExpress: Boolean, - // MissDetectionConfig - sampleMissDetectionEnabled: Boolean, - sampleMissDetectionEnableHeartbeat: Boolean, - sampleMissDetectionHeartbeatMs: Long, - sampleMissDetectionHeartbeatIsSporadic: Boolean, - - publisherDetection: Boolean, - ): Long - - @Throws(ZError::class) - private external fun declareSubscriberViaJNI( - keyExprPtr: Long, - keyExprString: String, - sessionPtr: Long, - callback: JNISubscriberCallback, - onClose: JNIOnCloseCallback, - ): Long - - @Throws(ZError::class) - private external fun declareAdvancedSubscriberViaJNI( - keyExprPtr: Long, - keyExprString: String, - sessionPtr: Long, - // HistoryConfig - historyConfigEnabled: Boolean, - historyDetectLatePublishers: Boolean, - historyMaxSamples: Long, - historyMaxAgeSeconds: Double, - // RecoveryConfig - recoveryConfigEnabled: Boolean, - recoveryConfigIsHeartbeat: Boolean, - recoveryQueryPeriodMs: Long, - - subscriberDetection: Boolean, - callback: JNISubscriberCallback, - onClose: JNIOnCloseCallback, - ): Long - - @Throws(ZError::class) - private external fun declareQueryableViaJNI( - keyExprPtr: Long, - keyExprString: String, - sessionPtr: Long, - callback: JNIQueryableCallback, - onClose: JNIOnCloseCallback, - complete: Boolean - ): Long - - @Throws(ZError::class) - private external fun declareQuerierViaJNI( - keyExprPtr: Long, - keyExprString: String, - sessionPtr: Long, - target: Int, - consolidation: Int, - congestionControl: Int, - priority: Int, - express: Boolean, - timeoutMs: Long, - acceptReplies: Int - ): Long - - @Throws(ZError::class) - private external fun declareKeyExprViaJNI(sessionPtr: Long, keyExpr: String): Long - - @Throws(ZError::class) - private external fun undeclareKeyExprViaJNI(sessionPtr: Long, keyExprPtr: Long) - - @Throws(ZError::class) - private external fun getViaJNI( - keyExprPtr: Long, - keyExprString: String, - selectorParams: String?, - sessionPtr: Long, - callback: JNIGetCallback, - onClose: JNIOnCloseCallback, - timeoutMs: Long, - target: Int, - consolidation: Int, - attachmentBytes: ByteArray?, - payload: ByteArray?, - encodingId: Int, - encodingSchema: String?, - congestionControl: Int, - priority: Int, - express: Boolean, - acceptReplies: Int, - ) - - @Throws(ZError::class) - private external fun putViaJNI( - keyExprPtr: Long, - keyExprString: String, - sessionPtr: Long, - valuePayload: ByteArray, - valueEncoding: Int, - valueEncodingSchema: String?, - congestionControl: Int, - priority: Int, - express: Boolean, - attachmentBytes: ByteArray?, - reliability: Int - ) - - @Throws(ZError::class) - private external fun deleteViaJNI( - keyExprPtr: Long, - keyExprString: String, - sessionPtr: Long, - congestionControl: Int, - priority: Int, - express: Boolean, - attachmentBytes: ByteArray?, - reliability: Int - ) -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISubscriber.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISubscriber.kt deleted file mode 100644 index 957c13e1..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNISubscriber.kt +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.pubsub.Subscriber - -/** - * Adapter class to handle the interactions with Zenoh through JNI for a [Subscriber] - * - * @property ptr: raw pointer to the underlying native Subscriber. - */ -internal class JNISubscriber(private val ptr: Long) { - - fun close() { - freePtrViaJNI(ptr) - } - - /** Frees the underlying native Subscriber. */ - private external fun freePtrViaJNI(ptr: Long) - -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIZBytes.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIZBytes.kt deleted file mode 100644 index 8e9e94b2..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIZBytes.kt +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.ZenohLoad -import io.zenoh.bytes.ZBytes -import kotlin.reflect.KType - -@PublishedApi -internal object JNIZBytes { - - init { - ZenohLoad - } - - external fun serializeViaJNI(any: Any, kType: KType): ZBytes - - external fun deserializeViaJNI(zBytes: ZBytes, kType: KType): Any -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIZenohId.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIZenohId.kt deleted file mode 100644 index 53ecb5dc..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/JNIZenohId.kt +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.ZenohLoad - -internal object JNIZenohId { - - init { - ZenohLoad - } - - external fun toStringViaJNI(bytes: ByteArray): String - -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt deleted file mode 100644 index 14f3c8e9..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni.callbacks - -internal fun interface JNIGetCallback { - - fun run( - replierZid: ByteArray?, - replierEid: Int, - success: Boolean, - keyExpr: String?, - payload: ByteArray, - encodingId: Int, - encodingSchema: String?, - kind: Int, - timestampNTP64: Long, - timestampIsValid: Boolean, - attachment: ByteArray?, - express: Boolean, - priority: Int, - congestionControl: Int, - ) -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIMatchingListenerCallback.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIMatchingListenerCallback.kt deleted file mode 100644 index d056c47d..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIMatchingListenerCallback.kt +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright (c) 2025 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni.callbacks - -internal fun interface JNIMatchingListenerCallback { - fun run( - matching: Boolean, - ) -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIOnCloseCallback.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIOnCloseCallback.kt deleted file mode 100644 index b58fa23d..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIOnCloseCallback.kt +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni.callbacks - -internal fun interface JNIOnCloseCallback { - - fun run() - -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt deleted file mode 100644 index 31f5885f..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni.callbacks - -internal fun interface JNIQueryableCallback { - fun run(keyExpr: String, - selectorParams: String, - payload: ByteArray?, - encodingId: Int, - encodingSchema: String?, - attachmentBytes: ByteArray?, - queryPtr: Long) -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISampleMissListenerCallback.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISampleMissListenerCallback.kt deleted file mode 100644 index a2111f4b..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISampleMissListenerCallback.kt +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright (c) 2025 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni.callbacks - -internal fun interface JNISampleMissListenerCallback { - fun run( - zidLower: Long, - zidUpper: Long, - eid: Long, - missedCount: Long - ) -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIScoutCallback.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIScoutCallback.kt deleted file mode 100644 index 0a8b20e9..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIScoutCallback.kt +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni.callbacks - -internal fun interface JNIScoutCallback { - - fun run(whatAmI: Int, zid: ByteArray, locators: List) -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt deleted file mode 100644 index 76373c72..00000000 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni.callbacks - -internal fun interface JNISubscriberCallback { - fun run( - keyExpr: String, - payload: ByteArray, - encodingId: Int, - encodingSchema: String?, - kind: Int, - timestampNTP64: Long, - timestampIsValid: Boolean, - attachment: ByteArray?, - express: Boolean, - priority: Int, - congestionControl: Int, - ) -} diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt index 4a2cae3a..0085139c 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt @@ -70,7 +70,7 @@ class KeyExpr internal constructor(internal val keyExpr: String, internal var jn * @return a [Result] with the [KeyExpr] in case of success. */ fun tryFrom(keyExpr: String) : Result { - return JNIKeyExpr.tryFrom(keyExpr) + return runCatching { KeyExpr(JNIKeyExpr.tryFrom(keyExpr)) } } /** @@ -83,7 +83,7 @@ class KeyExpr internal constructor(internal val keyExpr: String, internal var jn * @return a [Result] with the canonized [KeyExpr] in case of success. */ fun autocanonize(keyExpr: String): Result { - return JNIKeyExpr.autocanonize(keyExpr) + return runCatching { KeyExpr(JNIKeyExpr.autocanonize(keyExpr)) } } } @@ -93,7 +93,7 @@ class KeyExpr internal constructor(internal val keyExpr: String, internal var jn * This method returns `True` if there exists at least one key that belongs to both sets defined by `this` and the `other` key expressions. */ fun intersects(other: KeyExpr): Boolean { - return JNIKeyExpr.intersects(this, other) + return JNIKeyExpr.intersects(this.jniKeyExpr, this.keyExpr, other.jniKeyExpr, other.keyExpr) } /** @@ -102,7 +102,7 @@ class KeyExpr internal constructor(internal val keyExpr: String, internal var jn * This method returns `true` when all the keys defined by `other` also belong to the set defined by `this`. */ fun includes(other: KeyExpr): Boolean { - return JNIKeyExpr.includes(this, other) + return JNIKeyExpr.includes(this.jniKeyExpr, this.keyExpr, other.jniKeyExpr, other.keyExpr) } /** @@ -111,7 +111,7 @@ class KeyExpr internal constructor(internal val keyExpr: String, internal var jn * so you should favor these methods for most applications. */ fun relationTo(other: KeyExpr): SetIntersectionLevel { - return JNIKeyExpr.relationTo(this, other) + return SetIntersectionLevel.fromInt(JNIKeyExpr.relationTo(this.jniKeyExpr, this.keyExpr, other.jniKeyExpr, other.keyExpr)) } /** @@ -119,7 +119,7 @@ class KeyExpr internal constructor(internal val keyExpr: String, internal var jn * This should be your preferred method when concatenating path segments. */ fun join(other: String): Result { - return JNIKeyExpr.joinViaJNI(this, other) + return runCatching { KeyExpr(JNIKeyExpr.join(this.jniKeyExpr, this.keyExpr, other)) } } /** @@ -127,7 +127,7 @@ class KeyExpr internal constructor(internal val keyExpr: String, internal var jn * You should probably prefer [join] as Zenoh may then take advantage of the hierarchical separation it inserts. */ fun concat(other: String): Result { - return JNIKeyExpr.concatViaJNI(this, other) + return runCatching { KeyExpr(JNIKeyExpr.concat(this.jniKeyExpr, this.keyExpr, other)) } } override fun toString(): String { diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/AdvancedPublisher.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/AdvancedPublisher.kt index 765ba626..ead60816 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/AdvancedPublisher.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/AdvancedPublisher.kt @@ -17,6 +17,8 @@ package io.zenoh.pubsub import io.zenoh.annotations.Unstable import io.zenoh.exceptions.ZError import io.zenoh.jni.JNIAdvancedPublisher +import io.zenoh.jni.callbacks.JNIMatchingListenerCallback +import io.zenoh.jni.callbacks.JNIOnCloseCallback import io.zenoh.keyexpr.KeyExpr import io.zenoh.bytes.Encoding import io.zenoh.qos.QoS @@ -59,10 +61,15 @@ class AdvancedPublisher internal constructor( * */ fun declareMatchingListener(callback: MatchingCallback, onClose: (() -> Unit)? = null,): Result { - val resolvedOnClose = fun() { - onClose?.invoke() - } - return jniPublisher?.declareMatchingListener(callback, resolvedOnClose)?: invalidPublisherResult() + val resolvedOnClose = fun() { onClose?.invoke() } + return jniPublisher?.run { + runCatching { + MatchingListener(declareMatchingListener( + JNIMatchingListenerCallback { matching -> callback.run(matching) }, + JNIOnCloseCallback { resolvedOnClose() } + )) + } + } ?: invalidPublisherResult() } /** Declare [MatchingListener] for this publisher, specifying a handler to handle matching statuses. @@ -73,12 +80,16 @@ class AdvancedPublisher internal constructor( */ fun declareMatchingListener(handler: MatchingHandler, onClose: (() -> Unit)? = null,): Result { - val resolvedOnClose = fun() { - handler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { handler.onClose(); onClose?.invoke() } val callback = MatchingCallback { matching: Boolean -> handler.handle(matching) } - return jniPublisher?.declareMatchingListener(callback, resolvedOnClose)?: invalidPublisherResult() + return jniPublisher?.run { + runCatching { + MatchingListener(declareMatchingListener( + JNIMatchingListenerCallback { matching -> callback.run(matching) }, + JNIOnCloseCallback { resolvedOnClose() } + )) + } + } ?: invalidPublisherResult() } /** Declare [MatchingListener] for this publisher, specifying a [Channel] to pipe the received matching statuses. @@ -90,12 +101,16 @@ class AdvancedPublisher internal constructor( fun declareMatchingListener(channel: Channel, onClose: (() -> Unit)? = null,): Result { val channelHandler = MatchingChannelHandler(channel) - val resolvedOnClose = fun() { - channelHandler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { channelHandler.onClose(); onClose?.invoke() } val callback = MatchingCallback { matching: Boolean -> channelHandler.handle(matching) } - return jniPublisher?.declareMatchingListener(callback, resolvedOnClose)?: invalidPublisherResult() + return jniPublisher?.run { + runCatching { + MatchingListener(declareMatchingListener( + JNIMatchingListenerCallback { matching -> callback.run(matching) }, + JNIOnCloseCallback { resolvedOnClose() } + )) + } + } ?: invalidPublisherResult() } /** Declare background matching status listener for this [AdvancedPublisher] with callback @@ -105,10 +120,15 @@ class AdvancedPublisher internal constructor( * */ fun declareBackgroundMatchingListener(callback: MatchingCallback, onClose: (() -> Unit)? = null,): Result { - val resolvedOnClose = fun() { - onClose?.invoke() - } - return jniPublisher?.declareBackgroundMatchingListener(callback, resolvedOnClose)?: invalidPublisherResult() + val resolvedOnClose = fun() { onClose?.invoke() } + return jniPublisher?.run { + runCatching { + declareBackgroundMatchingListener( + JNIMatchingListenerCallback { matching -> callback.run(matching) }, + JNIOnCloseCallback { resolvedOnClose() } + ) + } + } ?: invalidPublisherResult() } /** Declare background matching status listener for this [AdvancedPublisher], specifying a handler to handle matching statuses. @@ -118,12 +138,16 @@ class AdvancedPublisher internal constructor( * */ fun declareBackgroundMatchingListener(handler: MatchingHandler, onClose: (() -> Unit)? = null,): Result { - val resolvedOnClose = fun() { - handler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { handler.onClose(); onClose?.invoke() } val callback = MatchingCallback { matching: Boolean -> handler.handle(matching) } - return jniPublisher?.declareBackgroundMatchingListener(callback, resolvedOnClose)?: invalidPublisherResult() + return jniPublisher?.run { + runCatching { + declareBackgroundMatchingListener( + JNIMatchingListenerCallback { matching -> callback.run(matching) }, + JNIOnCloseCallback { resolvedOnClose() } + ) + } + } ?: invalidPublisherResult() } /** Declare background matching status listener for this [AdvancedPublisher], specifying a [Channel] to pipe the received matching statuses. @@ -134,12 +158,16 @@ class AdvancedPublisher internal constructor( fun declareBackgroundMatchingListener(channel: Channel, onClose: (() -> Unit)? = null,): Result { val channelHandler = MatchingChannelHandler(channel) - val resolvedOnClose = fun() { - channelHandler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { channelHandler.onClose(); onClose?.invoke() } val callback = MatchingCallback { matching: Boolean -> channelHandler.handle(matching) } - return jniPublisher?.declareBackgroundMatchingListener(callback, resolvedOnClose)?: invalidPublisherResult() + return jniPublisher?.run { + runCatching { + declareBackgroundMatchingListener( + JNIMatchingListenerCallback { matching -> callback.run(matching) }, + JNIOnCloseCallback { resolvedOnClose() } + ) + } + } ?: invalidPublisherResult() } /** @@ -147,11 +175,17 @@ class AdvancedPublisher internal constructor( * * Will return true if there exist Subscribers matching the Publisher's key expression and false otherwise. */ - fun getMatchingStatus() = jniPublisher?.getMatchingStatus() ?: invalidPublisherResult() + fun getMatchingStatus(): Result { + return jniPublisher?.run { runCatching { getMatchingStatus() } } ?: invalidPublisherResult() + } /** Performs a PUT operation on the specified [keyExpr] with the specified [payload]. */ - fun put(payload: IntoZBytes, encoding: Encoding? = null, attachment: IntoZBytes? = null) = - jniPublisher?.put(payload, encoding ?: this.encoding, attachment) ?: invalidPublisherResult() + fun put(payload: IntoZBytes, encoding: Encoding? = null, attachment: IntoZBytes? = null): Result { + val enc = encoding ?: this.encoding + return jniPublisher?.run { + runCatching { put(payload.into().bytes, enc.id, enc.schema, attachment?.into()?.bytes) } + } ?: invalidPublisherResult() + } fun put(payload: String, encoding: Encoding? = null, attachment: String? = null) = put(ZBytes.from(payload), encoding, attachment?.let { ZBytes.from(attachment) }) @@ -159,7 +193,11 @@ class AdvancedPublisher internal constructor( /** * Performs a DELETE operation on the specified [keyExpr]. */ - fun delete(attachment: IntoZBytes? = null) = jniPublisher?.delete(attachment) ?: invalidPublisherResult() + fun delete(attachment: IntoZBytes? = null): Result { + return jniPublisher?.run { + runCatching { delete(attachment?.into()?.bytes) } + } ?: invalidPublisherResult() + } fun delete(attachment: String) = delete(ZBytes.from(attachment)) diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/AdvancedSubscriber.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/AdvancedSubscriber.kt index 198061e0..d1fddb81 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/AdvancedSubscriber.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/AdvancedSubscriber.kt @@ -15,6 +15,8 @@ package io.zenoh.pubsub import io.zenoh.annotations.Unstable +import io.zenoh.bytes.Encoding +import io.zenoh.bytes.ZBytes import io.zenoh.exceptions.ZError import io.zenoh.handlers.Callback import io.zenoh.handlers.ChannelHandler @@ -23,10 +25,18 @@ import io.zenoh.handlers.SampleMissCallback import io.zenoh.handlers.SampleMissChannelHandler import io.zenoh.handlers.SampleMissHandler import io.zenoh.jni.JNIAdvancedSubscriber +import io.zenoh.jni.callbacks.JNIOnCloseCallback +import io.zenoh.jni.callbacks.JNISampleMissListenerCallback +import io.zenoh.jni.callbacks.JNISubscriberCallback import io.zenoh.keyexpr.KeyExpr +import io.zenoh.qos.CongestionControl +import io.zenoh.qos.Priority +import io.zenoh.qos.QoS import io.zenoh.sample.Sample +import io.zenoh.sample.SampleKind import io.zenoh.session.SessionDeclaration import kotlinx.coroutines.channels.Channel +import org.apache.commons.net.ntp.TimeStamp /** * # Advanced Subscriber @@ -56,11 +66,14 @@ class AdvancedSubscriber internal constructor( history: Boolean = false, onClose: (() -> Unit)? = null ): Result> { - val resolvedOnClose = fun() { - onClose?.invoke() - } - return jniSubscriber?.declareDetectPublishersSubscriber(keyExpr, history, callback, resolvedOnClose, Unit)?: - invalidSubscriberResult() + val resolvedOnClose = fun() { onClose?.invoke() } + return jniSubscriber?.run { + runCatching { + Subscriber(keyExpr, Unit, declareDetectPublishersSubscriber( + history, makeSampleCallback(callback), JNIOnCloseCallback { resolvedOnClose() } + )) + } + } ?: invalidSubscriberResult() } /** Declares a subscriber to detect matching publishers. @@ -77,13 +90,15 @@ class AdvancedSubscriber internal constructor( onClose: (() -> Unit)? = null, receiver: R ): Result> { - val resolvedOnClose = fun() { - handler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { handler.onClose(); onClose?.invoke() } val callback = Callback { t: Sample -> handler.handle(t) } - return jniSubscriber?.declareDetectPublishersSubscriber(keyExpr, history,callback, resolvedOnClose, receiver)?: - invalidSubscriberResult() + return jniSubscriber?.run { + runCatching { + Subscriber(keyExpr, receiver, declareDetectPublishersSubscriber( + history, makeSampleCallback(callback), JNIOnCloseCallback { resolvedOnClose() } + )) + } + } ?: invalidSubscriberResult() } /** Declares a subscriber to detect matching publishers. @@ -101,13 +116,15 @@ class AdvancedSubscriber internal constructor( onClose: (() -> Unit)? = null, ): Result>> { val channelHandler = ChannelHandler(channel) - val resolvedOnClose = fun() { - channelHandler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { channelHandler.onClose(); onClose?.invoke() } val callback = Callback { t: Sample -> channelHandler.handle(t) } - return jniSubscriber?.declareDetectPublishersSubscriber(keyExpr, history,callback, resolvedOnClose, channelHandler.receiver())?: - invalidSubscriberResult() + return jniSubscriber?.run { + runCatching { + Subscriber(keyExpr, channelHandler.receiver(), declareDetectPublishersSubscriber( + history, makeSampleCallback(callback), JNIOnCloseCallback { resolvedOnClose() } + )) + } + } ?: invalidSubscriberResult() } /** Declares a background subscriber to detect matching publishers. @@ -125,11 +142,14 @@ class AdvancedSubscriber internal constructor( history: Boolean = false, onClose: (() -> Unit)? = null ): Result { - val resolvedOnClose = fun() { - onClose?.invoke() - } - return jniSubscriber?.declareBackgroundDetectPublishersSubscriber(keyExpr, history, callback, resolvedOnClose)?: - invalidSubscriberResult() + val resolvedOnClose = fun() { onClose?.invoke() } + return jniSubscriber?.run { + runCatching { + declareBackgroundDetectPublishersSubscriber( + history, makeSampleCallback(callback), JNIOnCloseCallback { resolvedOnClose() } + ) + } + } ?: invalidSubscriberResult() } /** Declares a background subscriber to detect matching publishers. @@ -147,13 +167,15 @@ class AdvancedSubscriber internal constructor( history: Boolean = false, onClose: (() -> Unit)? = null ): Result { - val resolvedOnClose = fun() { - handler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { handler.onClose(); onClose?.invoke() } val callback = Callback { t: Sample -> handler.handle(t) } - return jniSubscriber?.declareBackgroundDetectPublishersSubscriber(keyExpr, history,callback, resolvedOnClose)?: - invalidSubscriberResult() + return jniSubscriber?.run { + runCatching { + declareBackgroundDetectPublishersSubscriber( + history, makeSampleCallback(callback), JNIOnCloseCallback { resolvedOnClose() } + ) + } + } ?: invalidSubscriberResult() } /** Declares a background subscriber to detect matching publishers. @@ -173,13 +195,15 @@ class AdvancedSubscriber internal constructor( onClose: (() -> Unit)? = null, ): Result { val channelHandler = ChannelHandler(channel) - val resolvedOnClose = fun() { - channelHandler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { channelHandler.onClose(); onClose?.invoke() } val callback = Callback { t: Sample -> channelHandler.handle(t) } - return jniSubscriber?.declareBackgroundDetectPublishersSubscriber(keyExpr, history,callback, resolvedOnClose)?: - invalidSubscriberResult() + return jniSubscriber?.run { + runCatching { + declareBackgroundDetectPublishersSubscriber( + history, makeSampleCallback(callback), JNIOnCloseCallback { resolvedOnClose() } + ) + } + } ?: invalidSubscriberResult() } /** Declares a [SampleMissListener] to detect missed samples for ths [AdvancedSubscriber]. @@ -191,11 +215,15 @@ class AdvancedSubscriber internal constructor( * */ fun declareSampleMissListener(callback: SampleMissCallback, onClose: (() -> Unit)? = null,): Result { - val resolvedOnClose = fun() { - onClose?.invoke() - } - return jniSubscriber?.declareSampleMissListener(callback, resolvedOnClose)?: - invalidSubscriberResult() + val resolvedOnClose = fun() { onClose?.invoke() } + return jniSubscriber?.run { + runCatching { + SampleMissListener(declareSampleMissListener( + JNISampleMissListenerCallback { zidLower, zidUpper, eid, nb -> callback.run(SampleMiss(zidLower, zidUpper, eid, nb)) }, + JNIOnCloseCallback { resolvedOnClose() } + )) + } + } ?: invalidSubscriberResult() } /** Declares a [SampleMissListener] to detect missed samples for ths [AdvancedSubscriber]. @@ -207,13 +235,16 @@ class AdvancedSubscriber internal constructor( * */ fun declareSampleMissListener(handler: SampleMissHandler, onClose: (() -> Unit)? = null,): Result { - val resolvedOnClose = fun() { - handler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { handler.onClose(); onClose?.invoke() } val callback = SampleMissCallback { miss: SampleMiss -> handler.handle(miss) } - return jniSubscriber?.declareSampleMissListener(callback, resolvedOnClose)?: - invalidSubscriberResult() + return jniSubscriber?.run { + runCatching { + SampleMissListener(declareSampleMissListener( + JNISampleMissListenerCallback { zidLower, zidUpper, eid, nb -> callback.run(SampleMiss(zidLower, zidUpper, eid, nb)) }, + JNIOnCloseCallback { resolvedOnClose() } + )) + } + } ?: invalidSubscriberResult() } /** Declares a [SampleMissListener] to detect missed samples for ths [AdvancedSubscriber]. @@ -227,13 +258,16 @@ class AdvancedSubscriber internal constructor( fun declareSampleMissListener(channel: Channel, onClose: (() -> Unit)? = null,): Result { val channelHandler = SampleMissChannelHandler(channel) - val resolvedOnClose = fun() { - channelHandler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { channelHandler.onClose(); onClose?.invoke() } val callback = SampleMissCallback { miss: SampleMiss -> channelHandler.handle(miss) } - return jniSubscriber?.declareSampleMissListener(callback, resolvedOnClose)?: - invalidSubscriberResult() + return jniSubscriber?.run { + runCatching { + SampleMissListener(declareSampleMissListener( + JNISampleMissListenerCallback { zidLower, zidUpper, eid, nb -> callback.run(SampleMiss(zidLower, zidUpper, eid, nb)) }, + JNIOnCloseCallback { resolvedOnClose() } + )) + } + } ?: invalidSubscriberResult() } /** Declares a background sample miss listener to detect missed samples for ths [AdvancedSubscriber]. @@ -247,11 +281,15 @@ class AdvancedSubscriber internal constructor( * */ fun declareBackgroundSampleMissListener(callback: SampleMissCallback, onClose: (() -> Unit)? = null,): Result { - val resolvedOnClose = fun() { - onClose?.invoke() - } - return jniSubscriber?.declareBackgroundSampleMissListener(callback, resolvedOnClose)?: - invalidSubscriberResult() + val resolvedOnClose = fun() { onClose?.invoke() } + return jniSubscriber?.run { + runCatching { + declareBackgroundSampleMissListener( + JNISampleMissListenerCallback { zidLower, zidUpper, eid, nb -> callback.run(SampleMiss(zidLower, zidUpper, eid, nb)) }, + JNIOnCloseCallback { resolvedOnClose() } + ) + } + } ?: invalidSubscriberResult() } /** Declares a background sample miss listener to detect missed samples for ths [AdvancedSubscriber]. @@ -266,13 +304,16 @@ class AdvancedSubscriber internal constructor( * */ fun declareBackgroundSampleMissListener(handler: SampleMissHandler, onClose: (() -> Unit)? = null,): Result { - val resolvedOnClose = fun() { - handler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { handler.onClose(); onClose?.invoke() } val callback = SampleMissCallback { miss: SampleMiss -> handler.handle(miss) } - return jniSubscriber?.declareBackgroundSampleMissListener(callback, resolvedOnClose)?: - invalidSubscriberResult() + return jniSubscriber?.run { + runCatching { + declareBackgroundSampleMissListener( + JNISampleMissListenerCallback { zidLower, zidUpper, eid, nb -> callback.run(SampleMiss(zidLower, zidUpper, eid, nb)) }, + JNIOnCloseCallback { resolvedOnClose() } + ) + } + } ?: invalidSubscriberResult() } /** Declares a background sample miss listener to detect missed samples for ths [AdvancedSubscriber]. @@ -288,13 +329,28 @@ class AdvancedSubscriber internal constructor( fun declareBackgroundSampleMissListener(channel: Channel, onClose: (() -> Unit)? = null,): Result { val channelHandler = SampleMissChannelHandler(channel) - val resolvedOnClose = fun() { - channelHandler.onClose() - onClose?.invoke() - } + val resolvedOnClose = fun() { channelHandler.onClose(); onClose?.invoke() } val callback = SampleMissCallback { miss: SampleMiss -> channelHandler.handle(miss) } - return jniSubscriber?.declareBackgroundSampleMissListener(callback, resolvedOnClose)?: - invalidSubscriberResult() + return jniSubscriber?.run { + runCatching { + declareBackgroundSampleMissListener( + JNISampleMissListenerCallback { zidLower, zidUpper, eid, nb -> callback.run(SampleMiss(zidLower, zidUpper, eid, nb)) }, + JNIOnCloseCallback { resolvedOnClose() } + ) + } + } ?: invalidSubscriberResult() + } + + private fun makeSampleCallback(callback: Callback): JNISubscriberCallback { + return JNISubscriberCallback { keyExpr1, payload, encodingId, encodingSchema, kind, timestampNTP64, timestampIsValid, attachmentBytes, express, priority, congestionControl -> + val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null + callback.run(Sample( + KeyExpr(keyExpr1), ZBytes.from(payload), Encoding(encodingId, schema = encodingSchema), + SampleKind.fromInt(kind), timestamp, + QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), + attachmentBytes?.let { ZBytes.from(it) } + )) + } } diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/Publisher.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/Publisher.kt index 1a3def4b..84e4364e 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/Publisher.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/pubsub/Publisher.kt @@ -81,8 +81,12 @@ class Publisher internal constructor( fun priority() = qos.priority /** Performs a PUT operation on the specified [keyExpr] with the specified [payload]. */ - fun put(payload: IntoZBytes, encoding: Encoding? = null, attachment: IntoZBytes? = null) = - jniPublisher?.put(payload, encoding ?: this.encoding, attachment) ?: InvalidPublisherResult + fun put(payload: IntoZBytes, encoding: Encoding? = null, attachment: IntoZBytes? = null): Result { + val enc = encoding ?: this.encoding + return jniPublisher?.run { + runCatching { put(payload.into().bytes, enc.id, enc.schema, attachment?.into()?.bytes) } + } ?: InvalidPublisherResult + } fun put(payload: String, encoding: Encoding? = null, attachment: String? = null) = put(ZBytes.from(payload), encoding, attachment?.let { ZBytes.from(attachment) }) @@ -90,7 +94,11 @@ class Publisher internal constructor( /** * Performs a DELETE operation on the specified [keyExpr]. */ - fun delete(attachment: IntoZBytes? = null) = jniPublisher?.delete(attachment) ?: InvalidPublisherResult + fun delete(attachment: IntoZBytes? = null): Result { + return jniPublisher?.run { + runCatching { delete(attachment?.into()?.bytes) } + } ?: InvalidPublisherResult + } fun delete(attachment: String) = delete(ZBytes.from(attachment)) diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Querier.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Querier.kt index deeb3759..b1e02833 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Querier.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Querier.kt @@ -18,15 +18,24 @@ import io.zenoh.annotations.Unstable import io.zenoh.bytes.Encoding import io.zenoh.bytes.IntoZBytes import io.zenoh.bytes.ZBytes +import io.zenoh.config.EntityGlobalId +import io.zenoh.config.ZenohId import io.zenoh.exceptions.ZError import io.zenoh.handlers.Callback import io.zenoh.handlers.ChannelHandler import io.zenoh.handlers.Handler import io.zenoh.jni.JNIQuerier +import io.zenoh.jni.callbacks.JNIGetCallback +import io.zenoh.jni.callbacks.JNIOnCloseCallback import io.zenoh.keyexpr.KeyExpr +import io.zenoh.qos.CongestionControl +import io.zenoh.qos.Priority import io.zenoh.qos.QoS +import io.zenoh.sample.Sample +import io.zenoh.sample.SampleKind import io.zenoh.session.SessionDeclaration import kotlinx.coroutines.channels.Channel +import org.apache.commons.net.ntp.TimeStamp /** * A querier that allows to send queries to a [Queryable]. @@ -72,16 +81,7 @@ class Querier internal constructor(val keyExpr: KeyExpr, val qos: QoS, private v attachment: IntoZBytes? = null ): Result> { val handler = ChannelHandler(channel) - return jniQuerier?.performGet( - keyExpr, - parameters, - handler::handle, - handler::onClose, - handler.receiver(), - attachment, - payload, - encoding - ) ?: throw ZError("Querier is not valid.") + return performGet(parameters, handler::handle, handler::onClose, handler.receiver(), attachment, payload, encoding) } fun get( @@ -110,16 +110,7 @@ class Querier internal constructor(val keyExpr: KeyExpr, val qos: QoS, private v encoding: Encoding? = null, attachment: IntoZBytes? = null ): Result { - return jniQuerier?.performGet( - keyExpr, - parameters, - callback, - {}, - Unit, - attachment, - payload, - encoding - ) ?: throw ZError("Querier is not valid.") + return performGet(parameters, callback, {}, Unit, attachment, payload, encoding) } fun get( @@ -148,16 +139,7 @@ class Querier internal constructor(val keyExpr: KeyExpr, val qos: QoS, private v encoding: Encoding? = null, attachment: IntoZBytes? = null ): Result { - return jniQuerier?.performGet( - keyExpr, - parameters, - handler::handle, - handler::onClose, - handler.receiver(), - attachment, - payload, - encoding - ) ?: throw ZError("Querier is not valid.") + return performGet(parameters, handler::handle, handler::onClose, handler.receiver(), attachment, payload, encoding) } fun get( @@ -199,4 +181,47 @@ class Querier internal constructor(val keyExpr: KeyExpr, val qos: QoS, private v undeclare() } + private fun performGet( + parameters: Parameters?, + callback: Callback, + onClose: () -> Unit, + receiver: R, + attachment: IntoZBytes?, + payload: IntoZBytes?, + encoding: Encoding? + ): Result { + return jniQuerier?.run { + runCatching { + val jniCallback = JNIGetCallback { replierZid, replierEid, success, replyKeyExpr, replyPayload, encodingId, encodingSchema, kind, timestampNTP64, timestampIsValid, replyAttachment, express, priority, congestionControl -> + val reply = if (success) { + val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null + Reply( + replierZid?.let { EntityGlobalId(ZenohId(it), replierEid.toUInt()) }, + Result.success(Sample( + KeyExpr(replyKeyExpr!!), ZBytes.from(replyPayload), + Encoding(encodingId, schema = encodingSchema), + SampleKind.fromInt(kind), timestamp, + QoS(CongestionControl.fromInt(congestionControl), Priority.fromInt(priority), express), + replyAttachment?.let { ZBytes.from(it) } + )) + ) + } else { + Reply( + replierZid?.let { EntityGlobalId(ZenohId(it), replierEid.toUInt()) }, + Result.failure(ReplyError(ZBytes.from(replyPayload), Encoding(encodingId, schema = encodingSchema))) + ) + } + callback.run(reply) + } + val resolvedEncoding = encoding ?: Encoding.default() + get( + keyExpr.jniKeyExpr, keyExpr.keyExpr, parameters?.toString(), + jniCallback, JNIOnCloseCallback { onClose() }, + attachment?.into()?.bytes, payload?.into()?.bytes, + resolvedEncoding.id, resolvedEncoding.schema + ) + receiver + } + } ?: throw ZError("Querier is not valid.") + } } diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Query.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Query.kt index 5d56aa55..b76403e4 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Query.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/Query.kt @@ -44,7 +44,8 @@ class Query internal constructor( val payload: ZBytes?, val encoding: Encoding?, val attachment: ZBytes?, - private var jniQuery: JNIQuery? + private var jniQuery: JNIQuery?, + private val acceptRepliesValue: ReplyKeyExpr = ReplyKeyExpr.MATCHING_QUERY ) : AutoCloseable, ZenohType { /** Shortcut to the [selector]'s parameters. */ @@ -72,9 +73,15 @@ class Query internal constructor( timestamp: TimeStamp? = null, attachment: IntoZBytes? = null ): Result { - val sample = Sample(keyExpr, payload.into(), encoding, SampleKind.PUT, timestamp, qos.toQoS(), attachment?.into()) return jniQuery?.let { - val result = it.replySuccess(sample) + val result = runCatching { + it.replySuccess( + keyExpr.jniKeyExpr, keyExpr.keyExpr, + payload.into().bytes, encoding.id, encoding.schema, + timestamp != null, timestamp?.ntpValue() ?: 0L, + attachment?.into()?.bytes, qos.express + ) + } jniQuery = null result } ?: Result.failure(ZError("Query is invalid")) @@ -128,7 +135,7 @@ class Query internal constructor( */ fun replyErr(error: IntoZBytes, encoding: Encoding = Encoding.default()): Result { return jniQuery?.let { - val result = it.replyError(error, encoding) + val result = runCatching { it.replyError(error.into().bytes, encoding.id, encoding.schema) } jniQuery = null result } ?: Result.failure(ZError("Query is invalid")) @@ -157,7 +164,13 @@ class Query internal constructor( attachment: IntoZBytes? = null ): Result { return jniQuery?.let { - val result = it.replyDelete(keyExpr, timestamp, attachment, qos.toQoS()) + val result = runCatching { + it.replyDelete( + keyExpr.jniKeyExpr, keyExpr.keyExpr, + timestamp != null, timestamp?.ntpValue() ?: 0L, + attachment?.into()?.bytes, qos.express + ) + } jniQuery = null result } ?: Result.failure(ZError("Query is invalid")) @@ -173,13 +186,7 @@ class Query internal constructor( /** * Returns the [ReplyKeyExpr] accepted by this query. */ - fun acceptsReplies(): ReplyKeyExpr { - return if (selector.parameters?.containsKey(REPLY_KEY_EXPR_ANY_SEL_PARAM) == true) { - ReplyKeyExpr.ANY - } else { - ReplyKeyExpr.MATCHING_QUERY - } - } + fun acceptsReplies(): ReplyKeyExpr = acceptRepliesValue @Deprecated( message = "Use replyDel with ReplyQoS instead of QoS. Priority and congestion control are not applicable to replies.", replaceWith = ReplaceWith("replyDel(keyExpr, ReplyQoS(express = qos.express), timestamp, attachment)", "io.zenoh.qos.ReplyQoS") @@ -209,7 +216,5 @@ class Query internal constructor( } } - companion object { - private const val REPLY_KEY_EXPR_ANY_SEL_PARAM = "_anyke" - } + companion object } diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/ReplyKeyExpr.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/ReplyKeyExpr.kt index 7e6568c5..cb2c252a 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/ReplyKeyExpr.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/query/ReplyKeyExpr.kt @@ -18,12 +18,12 @@ package io.zenoh.query enum class ReplyKeyExpr { /** - * Replies may have any key expression. + * Replies must have a key expression matching the query's. */ - ANY, + MATCHING_QUERY, /** - * Replies must have a key expression matching the query's. + * Replies may have any key expression. */ - MATCHING_QUERY; + ANY; } diff --git a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/AdvancedPubSubTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/AdvancedPubSubTest.kt index 6f15da78..9c859ace 100644 --- a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/AdvancedPubSubTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/AdvancedPubSubTest.kt @@ -74,9 +74,6 @@ class AdvancedPubSubTest { @AfterTest fun tearDown() { - assertTrue(matchingSamples.count() != 0) - assertTrue(hasMatchingSubscribers) - matchingListener.close() publisher.close() @@ -86,6 +83,11 @@ class AdvancedPubSubTest { session.close() keyExpr.close() + + // session.close() should wait for pending callbacks to complete + // so checking the state of the test after closing the session + assertTrue(matchingSamples.count() != 0) + assertTrue(hasMatchingSubscribers) } @Test diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/ext/ZDeserialize.kt b/zenoh-kotlin/src/jvmAndAndroidMain/kotlin/io/zenoh/ext/ZDeserialize.kt similarity index 94% rename from zenoh-kotlin/src/commonMain/kotlin/io/zenoh/ext/ZDeserialize.kt rename to zenoh-kotlin/src/jvmAndAndroidMain/kotlin/io/zenoh/ext/ZDeserialize.kt index 93d86a4c..cd00c867 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/ext/ZDeserialize.kt +++ b/zenoh-kotlin/src/jvmAndAndroidMain/kotlin/io/zenoh/ext/ZDeserialize.kt @@ -1,7 +1,7 @@ package io.zenoh.ext import io.zenoh.bytes.ZBytes -import io.zenoh.jni.JNIZBytes.deserializeViaJNI +import io.zenoh.jni.JNIZBytesKotlin import kotlin.reflect.typeOf /** @@ -72,5 +72,5 @@ import kotlin.reflect.typeOf * @return a [Result] with the deserialization. */ inline fun zDeserialize(zbytes: ZBytes): Result = runCatching { - deserializeViaJNI(zbytes, typeOf()) as T + JNIZBytesKotlin.deserialize(zbytes.toBytes(), typeOf()) as T } diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/ext/ZSerialize.kt b/zenoh-kotlin/src/jvmAndAndroidMain/kotlin/io/zenoh/ext/ZSerialize.kt similarity index 95% rename from zenoh-kotlin/src/commonMain/kotlin/io/zenoh/ext/ZSerialize.kt rename to zenoh-kotlin/src/jvmAndAndroidMain/kotlin/io/zenoh/ext/ZSerialize.kt index e4d9b224..67008161 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/ext/ZSerialize.kt +++ b/zenoh-kotlin/src/jvmAndAndroidMain/kotlin/io/zenoh/ext/ZSerialize.kt @@ -1,7 +1,7 @@ package io.zenoh.ext import io.zenoh.bytes.ZBytes -import io.zenoh.jni.JNIZBytes.serializeViaJNI +import io.zenoh.jni.JNIZBytesKotlin import kotlin.reflect.typeOf /** @@ -72,5 +72,5 @@ import kotlin.reflect.typeOf * @return a [Result] with the serialized [ZBytes]. */ inline fun zSerialize(t: T): Result = runCatching { - serializeViaJNI(t, typeOf()) + ZBytes.from(JNIZBytesKotlin.serialize(t, typeOf())) } diff --git a/zenoh-kotlin/src/jvmMain/kotlin/io/zenoh/Target.kt b/zenoh-kotlin/src/jvmMain/kotlin/io/zenoh/Target.kt deleted file mode 100644 index f3f28256..00000000 --- a/zenoh-kotlin/src/jvmMain/kotlin/io/zenoh/Target.kt +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh - -internal enum class Target { - WINDOWS_X86_64_MSVC, - WINDOWS_AARCH64_MSVC, - LINUX_X86_64, - LINUX_AARCH64, - APPLE_AARCH64, - APPLE_X86_64; - - override fun toString(): String { - return when (this) { - WINDOWS_X86_64_MSVC -> "x86_64-pc-windows-msvc" - WINDOWS_AARCH64_MSVC -> "aarch64-pc-windows-msvc" - LINUX_X86_64 -> "x86_64-unknown-linux-gnu" - LINUX_AARCH64 -> "aarch64-unknown-linux-gnu" - APPLE_AARCH64 -> "aarch64-apple-darwin" - APPLE_X86_64 -> "x86_64-apple-darwin" - } - } -} diff --git a/zenoh-kotlin/src/jvmMain/kotlin/io/zenoh/Zenoh.kt b/zenoh-kotlin/src/jvmMain/kotlin/io/zenoh/Zenoh.kt deleted file mode 100644 index 8da47656..00000000 --- a/zenoh-kotlin/src/jvmMain/kotlin/io/zenoh/Zenoh.kt +++ /dev/null @@ -1,184 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh - -import java.io.File -import java.io.FileInputStream -import java.io.FileOutputStream -import java.io.InputStream -import java.util.zip.ZipInputStream - -/** - * Static singleton class to load the Zenoh native library once and only once, as well as the logger in function of the - * log level configuration. - */ -internal actual object ZenohLoad { - private const val ZENOH_LIB_NAME = "zenoh_jni" - - init { - // Try first to load the local native library for cases in which the module was built locally, - // otherwise try to load from the JAR. - if (tryLoadingLocalLibrary().isFailure) { - val target = determineTarget().getOrThrow() - tryLoadingLibraryFromJarPackage(target).getOrThrow() - } - } - - /** - * Determine target - * - * Determines the [Target] corresponding to the machine on top of which the native code will run. - * - * @return A result with the target. - */ - private fun determineTarget(): Result = runCatching { - val osName = System.getProperty("os.name").lowercase() - val osArch = System.getProperty("os.arch").lowercase() - - val target = when { - osName.contains("win") -> when { - osArch.contains("x86_64") || osArch.contains("amd64") || osArch.contains("x64") -> - Target.WINDOWS_X86_64_MSVC - - osArch.contains("aarch64") || osArch.contains("arm64") -> - Target.WINDOWS_AARCH64_MSVC - - else -> throw UnsupportedOperationException("Unsupported architecture on Windows: $osArch") - } - - osName.contains("mac") || osName.contains("darwin") || osName.contains("os x") -> when { - osArch.contains("x86_64") || osArch.contains("amd64") || osArch.contains("x64") -> - Target.APPLE_X86_64 - - osArch.contains("aarch64") || osArch.contains("arm64") -> - Target.APPLE_AARCH64 - - else -> throw UnsupportedOperationException("Unsupported architecture on macOS: $osArch") - } - - osName.contains("nix") || osName.contains("nux") || osName.contains("aix") -> when { - osArch.contains("x86_64") || osArch.contains("amd64") || osArch.contains("x64") -> - Target.LINUX_X86_64 - - osArch.contains("aarch64") || osArch.contains("arm64") -> - Target.LINUX_AARCH64 - - else -> throw UnsupportedOperationException("Unsupported architecture on Linux/Unix: $osArch") - } - - else -> throw UnsupportedOperationException("Unsupported platform: $osName") - } - return Result.success(target) - } - - /** - * Unzip library. - * - * The Zenoh libraries are stored within the JAR as compressed ZIP files. - * The location of the zipped files is expected to be under target/target.zip. - * It is expected that the zip file only contains the compressed library. - * - * The uncompressed library will be stored temporarily and deleted on exit. - * - * @param compressedLib Input stream pointing to the compressed library. - * @return A result with the uncompressed library file. - */ - private fun unzipLibrary(compressedLib: InputStream): Result = runCatching { - val zipInputStream = ZipInputStream(compressedLib) - val buffer = ByteArray(1024) - val zipEntry = zipInputStream.nextEntry - - val library = File.createTempFile(zipEntry!!.name, ".tmp") - library.deleteOnExit() - - val parent = library.parentFile - if (!parent.exists()) { - parent.mkdirs() - } - - val fileOutputStream = FileOutputStream(library) - var len: Int - while (zipInputStream.read(buffer).also { len = it } > 0) { - fileOutputStream.write(buffer, 0, len) - } - fileOutputStream.close() - - zipInputStream.closeEntry() - zipInputStream.close() - return Result.success(library) - } - - private fun loadLibraryAsInputStream(target: Target): Result = runCatching { - val targetName = "$target/$target.zip" - val libUrl = ClassLoader.getSystemClassLoader().getResourceAsStream(targetName) - ?: javaClass.classLoader.getResourceAsStream(targetName)!! - val uncompressedLibFile = unzipLibrary(libUrl) - return Result.success(FileInputStream(uncompressedLibFile.getOrThrow())) - } - - @Suppress("UnsafeDynamicallyLoadedCode") - private fun loadZenohJNI(inputStream: InputStream) { - val tempLib = File.createTempFile("tempLib", ".tmp") - tempLib.deleteOnExit() - - FileOutputStream(tempLib).use { output -> - inputStream.copyTo(output) - } - - System.load(tempLib.absolutePath) - } - - /** - * Load library from jar package. - * - * Attempts to load the library corresponding to the `target` specified from the zenoh kotlin jar. - * - * @param target - */ - private fun tryLoadingLibraryFromJarPackage(target: Target): Result = runCatching { - val lib: Result = loadLibraryAsInputStream(target) - lib.onSuccess { loadZenohJNI(it) }.onFailure { throw Exception("Unable to load Zenoh JNI: $it") } - } - - /** - * Try loading local library. - * - * This function aims to load the default library that is usually included when building the zenoh kotlin library - * locally. - */ - private fun tryLoadingLocalLibrary(): Result = runCatching { - val lib = ClassLoader.getSystemClassLoader().findLibraryStream(ZENOH_LIB_NAME) - ?: javaClass.classLoader.findLibraryStream( - ZENOH_LIB_NAME - ) - if (lib != null) { - loadZenohJNI(lib) - } else { - throw Exception("Unable to load local Zenoh JNI.") - } - } -} - -private fun ClassLoader.findLibraryStream(libraryName: String): InputStream? { - val libraryExtensions = listOf(".dylib", ".so", ".dll") - for (extension in libraryExtensions) { - val resourcePath = "lib$libraryName$extension" - val inputStream = getResourceAsStream(resourcePath) - if (inputStream != null) { - return inputStream - } - } - return null -}