diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..00f3324 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "mix" + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..73e6d11 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,144 @@ +name: CI + +env: + OTP_VERSION: 26.1.2 + ELIXIR_VERSION: 1.15.7 + MIX_ENV: test + +# based https://github.com/erlef/setup-beam + +on: + pull_request: + push: + branches: + - main + +jobs: + build-deps: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: erlef/setup-beam@v1 + id: setup-beam + with: + otp-version: ${{env.OTP_VERSION}} + elixir-version: ${{env.ELIXIR_VERSION}} + + - uses: actions/cache@v4 + id: save-deps-cache + with: + path: | + deps + _build + key: deps-${{ runner.os }}-${{ steps.setup-beam.outputs.otp-version }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ hashFiles('**/*.lock') }} + + - name: build deps + if: steps.save-deps-cache.outputs.cache-hit != 'true' + run: mix do deps.get, deps.compile + + code-analysis: + needs: build-deps + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: erlef/setup-beam@v1 + id: setup-beam + with: + otp-version: ${{env.OTP_VERSION}} + elixir-version: ${{env.ELIXIR_VERSION}} + + - uses: actions/cache/restore@v4 + id: restore-deps-cache + with: + path: | + deps + _build + key: deps-${{ runner.os }}-${{ steps.setup-beam.outputs.otp-version }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ hashFiles('**/*.lock') }} + restore-keys: deps-${{ runner.os }}-${{ steps.setup-beam.outputs.otp-version }}-${{ steps.setup-beam.outputs.elixir-version }}- + + - name: format + run: mix format --check-formatted + + - name: cargo fmt + working-directory: native/zenohex_nif + run: cargo fmt --all -- --check + + - name: credo + run: mix credo --ignore fixme + + - name: restore plts cache + id: restore-plts-cache + uses: actions/cache/restore@v4 + with: + key: plts-${{ runner.os }}-${{ steps.setup-beam.outputs.otp-version }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ hashFiles('**/*.lock') }} + restore-keys: plts-${{ runner.os }}-${{ steps.setup-beam.outputs.otp-version }}-${{ steps.setup-beam.outputs.elixir-version }}- + path: priv/plts + + - name: create plts + if: steps.restore-plts-cache.outputs.cache-hit != 'true' + run: mix dialyzer --plt + + - name: save plts cache + id: save-plts-cache + uses: actions/cache/save@v4 + if: steps.restore-plts-cache.outputs.cache-hit != 'true' + with: + key: plts-${{ runner.os }}-${{ steps.setup-beam.outputs.otp-version }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ hashFiles('**/*.lock') }} + path: priv/plts + + - name: dialyzer + run: mix dialyzer --format github + + test-with-one-session: + needs: build-deps + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: erlef/setup-beam@v1 + id: setup-beam + with: + otp-version: ${{env.OTP_VERSION}} + elixir-version: ${{env.ELIXIR_VERSION}} + + - uses: actions/cache/restore@v4 + id: restore-deps-cache + with: + path: | + deps + _build + key: deps-${{ runner.os }}-${{ steps.setup-beam.outputs.otp-version }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ hashFiles('**/*.lock') }} + restore-keys: deps-${{ runner.os }}-${{ steps.setup-beam.outputs.otp-version }}-${{ steps.setup-beam.outputs.elixir-version }}- + + - name: test + run: mix test --warnings-as-errors --cover + + test-with-another-session: + needs: build-deps + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: erlef/setup-beam@v1 + id: setup-beam + with: + otp-version: ${{env.OTP_VERSION}} + elixir-version: ${{env.ELIXIR_VERSION}} + + - uses: actions/cache/restore@v4 + id: restore-deps-cache + with: + path: | + deps + _build + key: deps-${{ runner.os }}-${{ steps.setup-beam.outputs.otp-version }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ hashFiles('**/*.lock') }} + restore-keys: deps-${{ runner.os }}-${{ steps.setup-beam.outputs.otp-version }}-${{ steps.setup-beam.outputs.elixir-version }}- + + - name: test + run: USE_DIFFERENT_SESSION="1" mix test --warnings-as-errors --cover diff --git a/.github/workflows/nif_precompile.yml b/.github/workflows/nif_precompile.yml index 0c5f3eb..c0f6504 100644 --- a/.github/workflows/nif_precompile.yml +++ b/.github/workflows/nif_precompile.yml @@ -12,20 +12,21 @@ jobs: strategy: fail-fast: false matrix: - nif: ["2.16", "2.15"] + nif: ["2.15"] job: - - { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04 , use-cross: true } - - { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04 , use-cross: true } - - { target: aarch64-apple-darwin , os: macos-11 } - - { target: x86_64-apple-darwin , os: macos-11 } - - { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04 } - - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04 , use-cross: true } - - { target: x86_64-pc-windows-gnu , os: windows-2019 } - - { target: x86_64-pc-windows-msvc , os: windows-2019 } + - { target: aarch64-apple-darwin , os: macos-12 } + - { target: aarch64-unknown-linux-gnu , os: ubuntu-22.04 , use-cross: true } + - { target: aarch64-unknown-linux-musl , os: ubuntu-22.04 , use-cross: true } + - { target: arm-unknown-linux-gnueabihf , os: ubuntu-22.04 , use-cross: true } + - { target: x86_64-apple-darwin , os: macos-12 } + - { target: x86_64-pc-windows-gnu , os: windows-2022 } + - { target: x86_64-pc-windows-msvc , os: windows-2022 } + - { target: x86_64-unknown-linux-gnu , os: ubuntu-22.04 } + - { target: x86_64-unknown-linux-musl , os: ubuntu-22.04 , use-cross: true } steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Extract project version shell: bash @@ -42,12 +43,12 @@ jobs: id: build-crate uses: philss/rustler-precompiled-action@v1.0.0 with: - project-name: nifzenoh + project-name: zenohex_nif project-version: ${{ env.PROJECT_VERSION }} target: ${{ matrix.job.target }} nif-version: ${{ matrix.nif }} use-cross: ${{ matrix.job.use-cross }} - project-dir: "native/nifzenoh" + project-dir: "native/zenohex_nif" - name: Artifact upload uses: actions/upload-artifact@v3 diff --git a/.gitignore b/.gitignore index 2f3db49..ebee2f0 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ zenohex-*.tar /tmp/ /priv/native/ + +/priv/plts/*.plt +/priv/plts/*.plt.hash diff --git a/.tool-versions b/.tool-versions index 9f88665..510eaf4 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -elixir 1.15.6-otp-26 -erlang 26.1.1 +elixir 1.15.7-otp-26 +erlang 26.1.2 diff --git a/README.md b/README.md index 9c58ad4..91e2902 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # Zenohex [![Hex version](https://img.shields.io/hexpm/v/zenohex.svg "Hex version")](https://hex.pm/packages/zenohex) -[![API docs](https://img.shields.io/hexpm/v/rclex.svg?label=hexdocs "API docs")](https://hexdocs.pm/zenohex/) +[![API docs](https://img.shields.io/hexpm/v/zenohex.svg?label=hexdocs "API docs")](https://hexdocs.pm/zenohex/) [![License](https://img.shields.io/hexpm/l/zenohex.svg)](https://github.com/zenohex/zenohex/blob/main/LICENSE) Zenohex is the [zenoh](https://zenoh.io/) client library for elixir. -Currently zenohex uses version 0.10.1-rc of zenoh. -If you want to communicate with Rust version of Zenoh, please use the same version. +**Currently zenohex uses version 0.10.1-rc of zenoh. +If you want to communicate with other Zenoh clients or routers, please use the same version.** ## Installation @@ -17,7 +17,7 @@ by adding `zenohex` to your list of dependencies in `mix.exs`: ```elixir def deps do [ - {:zenohex, "~> 0.1.5"} + {:zenohex, "~> 0.2.0-rc.2"} ] end ``` @@ -27,40 +27,55 @@ and published on [HexDocs](https://hexdocs.pm). Once published, the docs can be found at . ### Install rust (Optional) + Since version 0.1.3, Zenohex uses rustler precompiled and does not require Rust to be installed. -If you need to build rust NIFs code, please set the environment variable and install rust. -``` -export RUSTLER_PRECOMPILATION_EXAMPLE_BUILD=1 +If you want to build rust NIFs code, please add following to your config file. + +```elixir +config :rustler_precompiled, :force_build, zenohex: true ``` + https://www.rust-lang.org/tools/install ## Getting Started -### Publisher example -#### terminal 1 (Subscriber) -``` -iex -S mix -iex> NifZenoh.tester_sub "demo/example/zenoh-rs-pub" -``` +### Low layer Pub/Sub example -#### terminal 2 (Publisher) -``` -iex -S mix -iex> session = Zenohex.open -iex> {:ok, publisher} = Session.declare_publisher(session, "demo/example/zenoh-rs-pub") -iex> Publisher.put(publisher, "Hello zenoh?") +```sh +$ iex -S mix ``` -### Subscriber example -``` -(Subscriber) -iex -S mix -iex> session = Zenohex.open -iex> Session.declare_subscriber(session, "demo/example/zenoh-rs-pub", fn m -> IO.inspect(m) end) -(third argument is callback function) - -(Publisher) -iex> {:ok, publisher} = Session.declare_publisher(session, "demo/example/zenoh-rs-pub") -iex> Publisher.put(publisher, "Hello zenoh?") +```elixir +iex(1)> {:ok, session} = Zenohex.open() +{:ok, #Reference<>} +iex(2)> {:ok, publisher} = Zenohex.Session.declare_publisher(session, "pub/sub") +{:ok, #Reference<>} +iex(3)> {:ok, subscriber} = Zenohex.Session.declare_subscriber(session, "pub/sub") +{:ok, #Reference<>} +iex(4)> Zenohex.Publisher.put(publisher, "Hello Zenoh Dragon") +:ok +iex(5)> Zenohex.Subscriber.recv_timeout(subscriber, 1000) +{:ok, "Hello Zenoh Dragon"} +iex(6)> Zenohex.Subscriber.recv_timeout(subscriber, 1000) +{:error, :timeout} ``` + +### Practical examples + +We implemented practical examples under the [lib/zenohex/examples](https://github.com/b5g-ex/zenohex/tree/v0.2.0-rc.2/lib/zenohex/examples). + +Please read the [lib/zenohex/examples/README.md](https://github.com/b5g-ex/zenohex/tree/v0.2.0-rc.2/lib/zenohex/examples/README.md) to use them as your implementation's reference. + +## For developer + +### How to release + +1. Change versions, `mix.exs`, `native/zenohex_nif/Cargo.toml` +2. Run test, this step changes `native/zenohex_nif/Cargo.lock` version +3. Commit them and put the version tag, like v0.2.0-rc.2 +4. Puth the tag, like `git push origin v0.2.0-rc.2`. this step triggers the `.github/workflows/nif_precompile.yml` +5. After the artifacts are made, run `mix rustler_precompiled.download Zenohex.Nif --all` to update `checksum-Elixir.Zenohex.Nif.exs` and commit it. +6. Then publish to Hex + +(These steps just follows [Recommended flow](https://hexdocs.pm/rustler_precompiled/precompilation_guide.html#recommended-flow).) diff --git a/checksum-Elixir.NifZenoh.exs b/checksum-Elixir.NifZenoh.exs deleted file mode 100644 index c56f1fb..0000000 --- a/checksum-Elixir.NifZenoh.exs +++ /dev/null @@ -1,18 +0,0 @@ -%{ - "libnifzenoh-v0.1.3-nif-2.15-aarch64-apple-darwin.so.tar.gz" => "sha256:e575f80ec7f5158505d869fe41683840bd66589ac1a13931ed8e69571d24452b", - "libnifzenoh-v0.1.3-nif-2.15-aarch64-unknown-linux-gnu.so.tar.gz" => "sha256:b693ec00cff1a1b770f8175e3507db3fd7a31a096fe526e9dc213155d7b8c6ce", - "libnifzenoh-v0.1.3-nif-2.15-arm-unknown-linux-gnueabihf.so.tar.gz" => "sha256:10b19333605e6da1fef0193c906ecf4e39954e634aaf02087f85de9d21bb9c18", - "libnifzenoh-v0.1.3-nif-2.15-x86_64-apple-darwin.so.tar.gz" => "sha256:22f153adf7f777cf3630c75aec377906f82bb58836583a57a1cd2c4ee8fd9122", - "libnifzenoh-v0.1.3-nif-2.15-x86_64-unknown-linux-gnu.so.tar.gz" => "sha256:4a30af7dd557c2a87c70fc34b6ee4d41001f8ab5ea3b4cfbcbedb2ec5ac7cb6a", - "libnifzenoh-v0.1.3-nif-2.15-x86_64-unknown-linux-musl.so.tar.gz" => "sha256:895ab63756c41b12ae2ea3c152ccd029cec5f821d03635420a3eb606a2f71ef2", - "libnifzenoh-v0.1.3-nif-2.16-aarch64-apple-darwin.so.tar.gz" => "sha256:1ce1cf28a5f1c3e50eadec53f50df92637155f0fd9b1437fb775e84853f8dd20", - "libnifzenoh-v0.1.3-nif-2.16-aarch64-unknown-linux-gnu.so.tar.gz" => "sha256:62fe27ab4ac77e98b8ead32374c166f6060ceac7dbad0288e16530401795575e", - "libnifzenoh-v0.1.3-nif-2.16-arm-unknown-linux-gnueabihf.so.tar.gz" => "sha256:9413820ba7550a501b12d869b688e1714eb2f1ad0cf36f6c5189d76ae71c27e5", - "libnifzenoh-v0.1.3-nif-2.16-x86_64-apple-darwin.so.tar.gz" => "sha256:38414f9092a9f89f55f378bf55d39b29ec6edaa6b3490ba1681429fbd0fa179f", - "libnifzenoh-v0.1.3-nif-2.16-x86_64-unknown-linux-gnu.so.tar.gz" => "sha256:bed047c01f4410898c98e083f21f5dcb62731b3e414b257ade7f98c9e12f94f8", - "libnifzenoh-v0.1.3-nif-2.16-x86_64-unknown-linux-musl.so.tar.gz" => "sha256:27bba8210a88f3b601fea0f1ac328e7da10a083ab99b30ebc856578b2f54dc29", - "nifzenoh-v0.1.3-nif-2.15-x86_64-pc-windows-gnu.dll.tar.gz" => "sha256:645184368a09a1e418a11b03bf9581193fa90c9b1f6d4efb7a0d8ff237c04a39", - "nifzenoh-v0.1.3-nif-2.15-x86_64-pc-windows-msvc.dll.tar.gz" => "sha256:c4700abbf0408b2174e11cfc633d3661df1f4abb5f1b992e3e89cb9bb3f0839a", - "nifzenoh-v0.1.3-nif-2.16-x86_64-pc-windows-gnu.dll.tar.gz" => "sha256:f0efedb893f0a6b33a5fc665c02ced36fe4611b0b480507491b8cbb5abb894d3", - "nifzenoh-v0.1.3-nif-2.16-x86_64-pc-windows-msvc.dll.tar.gz" => "sha256:46bd8c0fc5cf35edbb95b6f4fa95cffa02c613c3756507d66400c2a88f147fde", -} diff --git a/checksum-Elixir.Zenohex.Nif.exs b/checksum-Elixir.Zenohex.Nif.exs new file mode 100644 index 0000000..4388fef --- /dev/null +++ b/checksum-Elixir.Zenohex.Nif.exs @@ -0,0 +1,11 @@ +%{ + "libzenohex_nif-v0.2.0-rc.2-nif-2.15-aarch64-apple-darwin.so.tar.gz" => "sha256:50123c9e497693e616daf10fcc3731329de3b2b25d7201bdfa06d4e1b430383e", + "libzenohex_nif-v0.2.0-rc.2-nif-2.15-aarch64-unknown-linux-gnu.so.tar.gz" => "sha256:d5fa11e706bdf974511c2a2ea4731e16460a272a5752ba9721201b3f6b435ae4", + "libzenohex_nif-v0.2.0-rc.2-nif-2.15-aarch64-unknown-linux-musl.so.tar.gz" => "sha256:4957bd71810dbff7d5685f3a6469cbf1adbe849e0e88c00607910441c701d0db", + "libzenohex_nif-v0.2.0-rc.2-nif-2.15-arm-unknown-linux-gnueabihf.so.tar.gz" => "sha256:db0dcbf78eea7fc7c8f9bfd6b9acd816da560c6ea345494195df8f9b967a2714", + "libzenohex_nif-v0.2.0-rc.2-nif-2.15-x86_64-apple-darwin.so.tar.gz" => "sha256:3413ab2f8f8772a690b59c43b6e5db5e3fcec721bc481ea14da20c890b22f561", + "libzenohex_nif-v0.2.0-rc.2-nif-2.15-x86_64-unknown-linux-gnu.so.tar.gz" => "sha256:c835b87013dc722c647851358deabd17abdc274c79b42fab617662ed06b8866c", + "libzenohex_nif-v0.2.0-rc.2-nif-2.15-x86_64-unknown-linux-musl.so.tar.gz" => "sha256:bc614603b0424618d5e18440cd9637b6deaf835d52f5523f936935b2f863a45f", + "zenohex_nif-v0.2.0-rc.2-nif-2.15-x86_64-pc-windows-gnu.dll.tar.gz" => "sha256:f4acdab20483a65e7e2f85aa139df5856e6ba1db7827e729af8b77fe46562779", + "zenohex_nif-v0.2.0-rc.2-nif-2.15-x86_64-pc-windows-msvc.dll.tar.gz" => "sha256:c9091043d80c1e6183002472cb2bc630767a783980665785836f8644921fae57", +} diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..51e395f --- /dev/null +++ b/config/config.exs @@ -0,0 +1,3 @@ +import Config + +config :rustler_precompiled, :force_build, zenohex: true diff --git a/lib/nif_zenoh.ex b/lib/nif_zenoh.ex deleted file mode 100644 index 0bebc9a..0000000 --- a/lib/nif_zenoh.ex +++ /dev/null @@ -1,59 +0,0 @@ -defmodule NifZenoh do - version = Mix.Project.config()[:version] - - use RustlerPrecompiled, - otp_app: :zenohex, - crate: "nifzenoh", - base_url: "https://github.com/b5g-ex/zenohex/releases/download/v#{version}", - force_build: System.get_env("RUSTLER_PRECOMPILATION_EXAMPLE_BUILD") in ["1", "true"], - targets: - Enum.uniq(["aarch64-unknown-linux-gnu" | RustlerPrecompiled.Config.default_targets()]), - version: version - - @type session() :: reference() - @type publisher() :: reference() - - @spec zenoh_open :: session() | no_return() - def zenoh_open(), do: :erlang.nif_error("NIF zenoh_open is not implemented") - - @spec session_declare_publisher(session(), charlist()) :: {:ok, publisher()} - def session_declare_publisher(_session, _keyexpr), - do: :erlang.nif_error("NIF session_declare_publisher is not implemented") - - @spec publisher_put_string(publisher(), charlist()) :: no_return() - def publisher_put_string(_publisher, _value), - do: :erlang.nif_error("NIF publisher_put is not implemented") - - @spec publisher_put_integer(publisher(), charlist()) :: no_return() - def publisher_put_integer(_publisher, _value), - do: :erlang.nif_error("NIF publisher_put is not implemented") - - @spec publisher_put_float(publisher(), charlist()) :: no_return() - def publisher_put_float(_publisher, _value), - do: :erlang.nif_error("NIF publisher_put is not implemented") - - @spec tester_pub(charlist(), charlist()) :: no_return() - def tester_pub(_keyexpr, _value), do: :erlang.nif_error("NIF tester_pub is not implemented") - - @spec tester_sub(charlist()) :: no_return() - def tester_sub(_keyexpr), do: :erlang.nif_error("NIF tester_sub is not implemented") - - @spec session_declare_subscriber(session(), charlist(), pid()) :: no_return() - def session_declare_subscriber(_session, _keyexpr, _callbackpid), - do: :erlang.nif_error("NIF session_declare_subscriber is not implemented") - - @spec session_declare_subscriber_wrapper(session(), charlist(), function()) :: no_return() - def session_declare_subscriber_wrapper(session, keyexpr, callback) do - pid = spawn(NifZenoh, :wait_message, [callback]) - - session_declare_subscriber(session, keyexpr, pid) - end - - def wait_message(callback) do - receive do - msg -> callback.(msg) - end - - wait_message(callback) - end -end diff --git a/lib/publisher.ex b/lib/publisher.ex deleted file mode 100644 index 74e63c4..0000000 --- a/lib/publisher.ex +++ /dev/null @@ -1,11 +0,0 @@ -defmodule Publisher do - @spec put(NifZenoh.publisher(), any) :: none - def put(publisher, value) do - cond do - is_binary(value) -> NifZenoh.publisher_put_string(publisher, value) - is_float(value) -> NifZenoh.publisher_put_float(publisher, value) - is_integer(value) -> NifZenoh.publisher_put_integer(publisher, value) - true -> :error - end - end -end diff --git a/lib/session.ex b/lib/session.ex deleted file mode 100644 index 852808a..0000000 --- a/lib/session.ex +++ /dev/null @@ -1,11 +0,0 @@ -defmodule Session do - @spec declare_publisher(NifZenoh.session(), charlist()) :: {:ok, NifZenoh.publisher()} - def declare_publisher(session, key) do - NifZenoh.session_declare_publisher(session, key) - end - - @spec declare_subscriber(NifZenoh.session(), charlist(), function()) :: no_return() - def declare_subscriber(session, keyexpr, callback) do - NifZenoh.session_declare_subscriber_wrapper(session, keyexpr, callback) - end -end diff --git a/lib/tester.ex b/lib/tester.ex deleted file mode 100644 index 28ef626..0000000 --- a/lib/tester.ex +++ /dev/null @@ -1,11 +0,0 @@ -defmodule Tester do - @spec pub(charlist(), charlist()) :: no_return() - def pub(keyexpr, value) do - NifZenoh.tester_pub(keyexpr, value) - end - - @spec sub(charlist()) :: no_return() - def sub(keyexpr) do - NifZenoh.tester_sub(keyexpr) - end -end diff --git a/lib/zenohex.ex b/lib/zenohex.ex index 902a0a9..a292c27 100644 --- a/lib/zenohex.ex +++ b/lib/zenohex.ex @@ -1,13 +1,41 @@ defmodule Zenohex do @moduledoc """ - Documentation for `Zenohex`. + Documentation for `#{__MODULE__}`. """ - @doc """ + alias Zenohex.Nif + alias Zenohex.Session + alias Zenohex.Config + alias Zenohex.Config.Scouting + @doc ~S""" + Open a zenoh Session. + + ## Examples + + iex> Zenohex.open() + """ + @spec open(Config.t()) :: {:ok, Session.t()} | {:error, reason :: any()} + def open(config \\ %Config{}) do + case System.get_env("SCOUTING_DELAY") do + nil -> + Nif.zenoh_open(config) + + delay -> + Nif.zenoh_open(%Config{config | scouting: %Scouting{delay: String.to_integer(delay)}}) + end + end + + @doc ~S""" + Open a zenoh Session. + + ## Examples + + iex> Zenohex.open!() """ - @spec open :: NifZenoh.session() - def open do - NifZenoh.zenoh_open() + @spec open!(Config.t()) :: Session.t() + def open!(config \\ %Config{}) do + {:ok, session} = open(config) + session end end diff --git a/lib/zenohex/config.ex b/lib/zenohex/config.ex new file mode 100644 index 0000000..03a18fb --- /dev/null +++ b/lib/zenohex/config.ex @@ -0,0 +1,13 @@ +defmodule Zenohex.Config do + @moduledoc """ + Documentation for `#{__MODULE__}`. + + Used by `Zenohex.open/1`, `Zenohex.open!/1`. + """ + + alias Zenohex.Config.Connect + alias Zenohex.Config.Scouting + + @type t :: %__MODULE__{connect: Connect.t(), scouting: Scouting.t()} + defstruct connect: %Connect{}, scouting: %Scouting{} +end diff --git a/lib/zenohex/config/connect.ex b/lib/zenohex/config/connect.ex new file mode 100644 index 0000000..21268cd --- /dev/null +++ b/lib/zenohex/config/connect.ex @@ -0,0 +1,14 @@ +defmodule Zenohex.Config.Connect do + @moduledoc """ + Documentation for `#{__MODULE__}`. + """ + + @type t :: %__MODULE__{endpoints: endpoints()} + + @typedoc """ + ex. ["tcp/192.168.1.1:7447", "tcp/192.168.1.2:7447"] + """ + @type endpoints() :: [String.t()] + + defstruct endpoints: [] +end diff --git a/lib/zenohex/config/scouting.ex b/lib/zenohex/config/scouting.ex new file mode 100644 index 0000000..c4fc20b --- /dev/null +++ b/lib/zenohex/config/scouting.ex @@ -0,0 +1,14 @@ +defmodule Zenohex.Config.Scouting do + @moduledoc """ + Documentation for `#{__MODULE__}`. + """ + + @type t :: %__MODULE__{delay: delay()} + + @typedoc """ + In peer mode, the period dedicated to scouting remote peers before attempting other operations. In milliseconds. + """ + @type delay :: non_neg_integer() | :undefined + + defstruct delay: :undefined +end diff --git a/lib/zenohex/examples/README.md b/lib/zenohex/examples/README.md new file mode 100644 index 0000000..db10b09 --- /dev/null +++ b/lib/zenohex/examples/README.md @@ -0,0 +1,237 @@ +# Zenohex.Examples + +This README shows how to use following each example implementations. + +- [Zenohex.Examples.Publisher](#publisher) +- [Zenohex.Examples.Subscriber](#subscriber) +- [Zenohex.Examples.PullSubscriber](#pullsubscriber) +- [Zenohex.Examples.Queryable](#queryable) +- [Zenohex.Examples.Session](#session) +- [Zenohex.Examples.Storage](#storage) + +## Publisher + +This Publisher is made of `Supervisor` and `GenServer`. +If you would like to see the codes, check the followings. + +- Supervisor + - [/lib/zenohex/examples/publisher.ex](/lib/zenohex/examples/publisher.ex) +- GenServer + - [/lib/zenohex/examples/publisher/impl.ex](/lib/zenohex/examples/publisher/impl.ex) + +### Start Publisher + +```elixir +iex> alias Zenohex.Examples.Publisher +# if not specify session and key_expr, they are made internally. key_expr is "zenohex/examples/pub" +iex> Publisher.start_link() +# you can also inject your session and key_expr from outside +iex> Publisher.start_link(%{session: your_session, key_expr: "your_key/expression"}) +``` + +### Put data + +```elixir +iex> Publisher.put(42) # integer +iex> Publisher.put(42.42) # float +iex> Publisher.put("42") # binary +``` + +### Delete data + +```elixir +iex> Publisher.delete() +``` + +see. [#16](https://github.com/b5g-ex/zenohex/issues/16) + +### Change Publisher options + +```elixir +iex> Publisher.congestion_control(:block) +iex> Publisher.priority(:real_time) +``` + +see. Supported options, [Zenohex.Publisher.Options](/lib/zenohex/publisher.ex) + +## Subscriber + +This Subscriber is made of `Supervisor` and `GenServer`. +If you would like to see the codes, check the followings. + +- Supervisor + - [lib/zenohex/examples/subscriber.ex](/lib/zenohex/examples/subscriber.ex) +- GenServer + - [lib/zenohex/examples/subscriber/impl.ex](/lib/zenohex/examples/subscriber/impl.ex) + +### Start Subscriber + +```elixir +iex> alias Zenohex.Examples.Subscriber +# if not specify session, key_expr and callback, they are made internally. key_expr is "zenohex/examples/**",callback is &Logger.debug(inspect(&1)) +iex> Subscriber.start_link() +# you can also inject your session, key_expr and callback from outside +iex> Subscriber.start_link(%{session: your_session, key_expr: "your_key/expression/**", callback: &IO.inspect/1}) +``` + +### Subscribed? + +```elixir +iex> alias Zenohex.Examples.Publisher +iex> Publisher.start_link() +iex> Publisher.put("subscribed?") +:ok + +11:51:53.959 [debug] %Zenohex.Sample{key_expr: "zenohex/examples/pub", value: "subscribed?", kind: :put, reference: #Reference<0.1373489635.746717252.118288>} +``` + +## PullSubscriber + +This PullSubscriber is made of `Supervisor` and `GenServer`. +If you would like to see the codes, check the followings. + +- Supervisor + - [lib/zenohex/examples/pull_subscriber.ex](/lib/zenohex/examples/pull_subscriber.ex) +- GenServer + - [lib/zenohex/examples/pull_subscriber/impl.ex](/lib/zenohex/examples/pull_subscriber/impl.ex) + +### Start PullSubscriber + +```elixir +iex> alias Zenohex.Examples.PullSubscriber +# if not specify session, key_expr and callback, they are made internally. key_expr is "zenohex/examples/**",callback is &Logger.debug(inspect(&1)) +iex> PullSubscriber.start_link() +# you can also inject your session, key_expr and callback from outside +iex> PullSubscriber.start_link(%{session: your_session, key_expr: "your_key/expression/**", callback: &IO.inspect/1}) +``` + +### Pull data + +```elixir +iex> alias Zenohex.Examples.Publisher +iex> Publisher.start_link() +iex> Publisher.put("subscribed?") +:ok +iex> PullSubscriber.pull() +:ok + +12:16:47.306 [debug] %Zenohex.Sample{key_expr: "zenohex/examples/pub", value: "subscribed?", kind: :put, reference: #Reference<0.662543409.1019347013.179304>} +``` + +## Queryable + +This Queryable is made of `Supervisor` and `GenServer`. +If you would like to see the codes, check the followings. + +- Supervisor + - [lib/zenohex/examples/queryable.ex](/lib/zenohex/examples/queryable.ex) +- GenServer + - [lib/zenohex/examples/queryable/impl.ex](/lib/zenohex/examples/queryable/impl.ex) + +### Start Queryable + +```elixir +iex> alias Zenohex.Examples.Queryable +# if not specify session, key_expr and callback, they are made internally. key_expr is "zenohex/examples/**", callback is &Logger.debug(inspect(&1)) +iex> Queryable.start_link() +# you can also inject your session, key_expr and callback from outside +iex> Queryable.start_link(%{session: your_session, key_expr: "your_key/expression/**", callback: &IO.inspect/1}) +``` + +### Queried? + +```elixir +iex> alias Zenohex.Examples.Session +iex> Session.start_link() +iex> Session.get("zenohex/examples/get", &IO.inspect/1) +:ok + +15:20:17.870 [debug] %Zenohex.Query{key_expr: "zenohex/examples/get", parameters: "", value: :undefined, reference: #Reference<0.3076585362.3463839816.144434>} +``` + +## Session + +This Session is made of `Supervisor` and `GenServer`. +If you would like to see the codes, check the followings. + +- Supervisor + - [lib/zenohex/examples/session.ex](/lib/zenohex/examples/session.ex) +- GenServer + - [lib/zenohex/examples/session/impl.ex](/lib/zenohex/examples/session/impl.ex) + +### Start Session + +```elixir +iex> alias Zenohex.Examples.Session +# if not specify session, it is made internally +iex> Session.start_link() +# you can also inject your session and key_expr from outside +iex> Session.start_link(%{session: your_session}) +``` + +### Put data + +```elixir +iex> Session.put("zenoh/example/session/put", 42) # integer +iex> Session.put("zenoh/example/session/put", 42.42) # float +iex> Session.put("zenoh/example/session/put", "42") # binary +``` + +### Delete data + +```elixir +iex> Session.delete("zenoh/example/session/put") +iex> Session.delete("zenoh/example/session/**") +``` + +### Get data + +```elixir +iex> callback = &IO.inspect/1 +iex> Session.get("zenoh/example/session/get", callback) +``` + +## Storage + +This Storage is made of `Supervisor` and `GenServer`. +If you would like to see the codes, check the followings. + +- Supervisor + - [lib/zenohex/examples/storage.ex](/lib/zenohex/examples/storage.ex) +- GenServer + - [lib/zenohex/examples/storage/store.ex](/lib/zenohex/examples/storage/store.ex) + - [lib/zenohex/examples/storage/subscriber.ex](/lib/zenohex/examples/storage/subscriber.ex) + - [lib/zenohex/examples/storage/queryable.ex](/lib/zenohex/examples/storage/queryable.ex) + +In this example, we made store with `Agent`. We think we can use also `:ets`, `:dets` and `:mnesia`. + +### Start Storage + +```elixir +iex> alias Zenohex.Examples.Storage +# if not specify session, key_expr and callback, they are made internally. key_expr is "zenohex/examples/**" +iex> Storage.start_link() +# you can also inject your session, key_expr and callback from outside +iex> Storage.start_link(%{session: your_session, key_expr: "your_key/expression/**"}) +``` + +### Session put/get/delete data with Storage + +```elixir +iex> alias Zenohex.Examples.Session +iex> Session.start_link() +iex> Session.put("zenoh/examples/storage", _value = "put") +:ok +iex> Session.get("zenoh/examples/storage", _callback = &IO.inspect/1) +:ok +%Zenohex.Sample{ + key_expr: "zenohex/examples/storage", + value: "put", + kind: :put, + reference: #Reference<0.2244884903.1591869505.81651> +} +iex> Session.delete("zenoh/examples/storage") +:ok +iex> Session.get("zenoh/examples/storage", &IO.inspect/1) +:ok +``` diff --git a/lib/zenohex/examples/publisher.ex b/lib/zenohex/examples/publisher.ex new file mode 100644 index 0000000..d2bf1e7 --- /dev/null +++ b/lib/zenohex/examples/publisher.ex @@ -0,0 +1,42 @@ +defmodule Zenohex.Examples.Publisher do + @moduledoc false + + use Supervisor + + alias Zenohex.Examples.Publisher + + @doc "Start Publisher." + def start_link(args \\ %{}) when is_map(args) do + session = Map.get(args, :session) || Zenohex.open!() + key_expr = Map.get(args, :key_expr, "zenohex/examples/pub") + Supervisor.start_link(__MODULE__, %{session: session, key_expr: key_expr}, name: __MODULE__) + end + + @doc "Put data." + @spec put(integer() | float() | binary()) :: :ok + defdelegate put(value), to: Publisher.Impl + + @doc "Delete data." + @spec delete() :: :ok + defdelegate delete(), to: Publisher.Impl + + @doc "Change congestion control." + @spec congestion_control(Zenohex.Publisher.Options.congestion_control()) :: :ok + defdelegate congestion_control(option), to: Publisher.Impl + + @doc "Change priority." + @spec priority(Zenohex.Publisher.Options.priority()) :: :ok + defdelegate priority(option), to: Publisher.Impl + + @doc false + def init(args) when is_map(args) do + children = [ + {Publisher.Impl, args} + ] + + Supervisor.init(children, strategy: :one_for_one) + end + + @doc false + def child_spec(args), do: super(args) +end diff --git a/lib/zenohex/examples/publisher/impl.ex b/lib/zenohex/examples/publisher/impl.ex new file mode 100644 index 0000000..fee730e --- /dev/null +++ b/lib/zenohex/examples/publisher/impl.ex @@ -0,0 +1,52 @@ +defmodule Zenohex.Examples.Publisher.Impl do + @moduledoc false + + use GenServer + + def start_link(args) do + GenServer.start_link(__MODULE__, args, name: __MODULE__) + end + + def put(value) do + GenServer.call(__MODULE__, {:put, value}) + end + + def delete() do + GenServer.call(__MODULE__, :delete) + end + + def congestion_control(value) do + GenServer.call(__MODULE__, {:congestion_control, value}) + end + + def priority(value) do + GenServer.call(__MODULE__, {:priority, value}) + end + + def init(args) do + session = Map.fetch!(args, :session) + key_expr = Map.fetch!(args, :key_expr) + {:ok, publisher} = Zenohex.Session.declare_publisher(session, key_expr) + {:ok, %{publisher: publisher}} + end + + def handle_call({:put, value}, _from, state) do + :ok = Zenohex.Publisher.put(state.publisher, value) + {:reply, :ok, state} + end + + def handle_call(:delete, _from, state) do + :ok = Zenohex.Publisher.delete(state.publisher) + {:reply, :ok, state} + end + + def handle_call({:congestion_control, value}, _from, state) do + publisher = Zenohex.Publisher.congestion_control(state.publisher, value) + {:reply, :ok, %{state | publisher: publisher}} + end + + def handle_call({:priority, value}, _from, state) do + publisher = Zenohex.Publisher.priority(state.publisher, value) + {:reply, :ok, %{state | publisher: publisher}} + end +end diff --git a/lib/zenohex/examples/pull_subscriber.ex b/lib/zenohex/examples/pull_subscriber.ex new file mode 100644 index 0000000..5d86cc3 --- /dev/null +++ b/lib/zenohex/examples/pull_subscriber.ex @@ -0,0 +1,36 @@ +defmodule Zenohex.Examples.PullSubscriber do + @moduledoc false + + use Supervisor + + require Logger + + alias Zenohex.Examples.PullSubscriber + + @doc "Start PullSubscriber." + def start_link(args \\ %{}) when is_map(args) do + session = Map.get(args, :session) || Zenohex.open!() + key_expr = Map.get(args, :key_expr, "zenohex/examples/**") + callback = Map.get(args, :callback, &Logger.debug(inspect(&1))) + + Supervisor.start_link(__MODULE__, %{session: session, key_expr: key_expr, callback: callback}, + name: __MODULE__ + ) + end + + @doc "Pull data." + @spec pull() :: :ok + defdelegate pull(), to: PullSubscriber.Impl + + @doc false + def init(args) when is_map(args) do + children = [ + {PullSubscriber.Impl, args} + ] + + Supervisor.init(children, strategy: :one_for_one) + end + + @doc false + def child_spec(args), do: super(args) +end diff --git a/lib/zenohex/examples/pull_subscriber/impl.ex b/lib/zenohex/examples/pull_subscriber/impl.ex new file mode 100644 index 0000000..29ee96b --- /dev/null +++ b/lib/zenohex/examples/pull_subscriber/impl.ex @@ -0,0 +1,52 @@ +defmodule Zenohex.Examples.PullSubscriber.Impl do + @moduledoc false + + use GenServer + + require Logger + + def start_link(args) do + GenServer.start_link(__MODULE__, args, name: __MODULE__) + end + + def pull() do + GenServer.call(__MODULE__, :pull) + end + + def init(args) do + session = Map.fetch!(args, :session) + key_expr = Map.fetch!(args, :key_expr) + callback = Map.fetch!(args, :callback) + + {:ok, pull_subscriber} = Zenohex.Session.declare_pull_subscriber(session, key_expr) + state = %{pull_subscriber: pull_subscriber, callback: callback} + + recv_timeout(state) + + {:ok, state} + end + + def handle_info(:loop, state) do + recv_timeout(state) + {:noreply, state} + end + + def handle_call(:pull, _from, state) do + :ok = Zenohex.PullSubscriber.pull(state.pull_subscriber) + {:reply, :ok, state} + end + + defp recv_timeout(state) do + case Zenohex.PullSubscriber.recv_timeout(state.pull_subscriber) do + {:ok, sample} -> + state.callback.(sample) + send(self(), :loop) + + {:error, :timeout} -> + send(self(), :loop) + + {:error, error} -> + Logger.error(inspect(error)) + end + end +end diff --git a/lib/zenohex/examples/queryable.ex b/lib/zenohex/examples/queryable.ex new file mode 100644 index 0000000..a986e1a --- /dev/null +++ b/lib/zenohex/examples/queryable.ex @@ -0,0 +1,32 @@ +defmodule Zenohex.Examples.Queryable do + @moduledoc false + + use Supervisor + + require Logger + + alias Zenohex.Examples.Queryable + + @doc "Start Queryable." + def start_link(args \\ %{}) when is_map(args) do + session = Map.get(args, :session) || Zenohex.open!() + key_expr = Map.get(args, :key_expr, "zenohex/examples/**") + callback = Map.get(args, :callback, &Logger.debug(inspect(&1))) + + Supervisor.start_link(__MODULE__, %{session: session, key_expr: key_expr, callback: callback}, + name: __MODULE__ + ) + end + + @doc false + def init(args) when is_map(args) do + children = [ + {Queryable.Impl, args} + ] + + Supervisor.init(children, strategy: :one_for_one) + end + + @doc false + def child_spec(args), do: super(args) +end diff --git a/lib/zenohex/examples/queryable/impl.ex b/lib/zenohex/examples/queryable/impl.ex new file mode 100644 index 0000000..10d600e --- /dev/null +++ b/lib/zenohex/examples/queryable/impl.ex @@ -0,0 +1,44 @@ +defmodule Zenohex.Examples.Queryable.Impl do + @moduledoc false + + use GenServer + + require Logger + + def start_link(args) do + GenServer.start_link(__MODULE__, args, name: __MODULE__) + end + + def init(args) do + session = Map.fetch!(args, :session) + key_expr = Map.fetch!(args, :key_expr) + callback = Map.fetch!(args, :callback) + + {:ok, queryable} = Zenohex.Session.declare_queryable(session, key_expr) + state = %{queryable: queryable, callback: callback} + + recv_timeout(state) + + {:ok, state} + end + + def handle_info(:loop, state) do + recv_timeout(state) + + {:noreply, state} + end + + def recv_timeout(state) do + case Zenohex.Queryable.recv_timeout(state.queryable) do + {:ok, query} -> + state.callback.(query) + send(self(), :loop) + + {:error, :timeout} -> + send(self(), :loop) + + {:error, error} -> + Logger.error(inspect(error)) + end + end +end diff --git a/lib/zenohex/examples/session.ex b/lib/zenohex/examples/session.ex new file mode 100644 index 0000000..42d5926 --- /dev/null +++ b/lib/zenohex/examples/session.ex @@ -0,0 +1,41 @@ +defmodule Zenohex.Examples.Session do + @moduledoc false + + use Supervisor + + alias Zenohex.Examples.Session + + @doc "Start Session." + def start_link(args \\ %{}) when is_map(args) do + session = Map.get(args, :session) || Zenohex.open!() + Supervisor.start_link(__MODULE__, %{session: session}, name: __MODULE__) + end + + @doc "Put data." + @spec put(String.t(), integer() | float() | binary()) :: :ok + defdelegate put(key_expr, value), to: Session.Impl + + @doc "Delete data." + @spec delete(String.t()) :: :ok + defdelegate delete(key_expr), to: Session.Impl + + @doc "Set disconnected callback." + @spec set_disconnected_cb(function) :: :ok + defdelegate set_disconnected_cb(callback), to: Session.Impl + + @doc "Get data." + @spec get(String.t(), function()) :: :ok + defdelegate get(selector, callback), to: Session.Impl + + @doc false + def init(args) when is_map(args) do + children = [ + {Session.Impl, args} + ] + + Supervisor.init(children, strategy: :one_for_one) + end + + @doc false + def child_spec(args), do: super(args) +end diff --git a/lib/zenohex/examples/session/impl.ex b/lib/zenohex/examples/session/impl.ex new file mode 100644 index 0000000..5495bc5 --- /dev/null +++ b/lib/zenohex/examples/session/impl.ex @@ -0,0 +1,77 @@ +defmodule Zenohex.Examples.Session.Impl do + @moduledoc false + + use GenServer + + require Logger + + def start_link(args) do + GenServer.start_link(__MODULE__, args, name: __MODULE__) + end + + def put(key_expr, value) do + GenServer.call(__MODULE__, {:put, key_expr, value}) + end + + def set_disconnected_cb(callback) do + GenServer.call(__MODULE__, {:set_disconnected_cb, callback}) + end + + def delete(key_expr) do + GenServer.call(__MODULE__, {:delete, key_expr}) + end + + def get(selector, callback) do + GenServer.call(__MODULE__, {:get, selector, callback}) + end + + def init(args) do + {:ok, + %{ + session: Map.fetch!(args, :session), + selector: nil, + receiver: nil, + callback: nil, + disconnected_cb: fn -> nil end + }} + end + + def handle_call({:put, key_expr, value}, _from, state) do + ret = Zenohex.Session.put(state.session, key_expr, value) + {:reply, ret, state} + end + + def handle_call({:set_disconnected_cb, callback}, _from, state) do + {:reply, :ok, %{state | disconnected_cb: callback}} + end + + def handle_call({:delete, key_expr}, _from, state) do + :ok = Zenohex.Session.delete(state.session, key_expr) + {:reply, :ok, state} + end + + def handle_call({:get, selector, callback}, _from, state) do + {:ok, receiver} = Zenohex.Session.get_reply_receiver(state.session, selector) + send(self(), :get_reply) + {:reply, :ok, %{state | selector: selector, receiver: receiver, callback: callback}} + end + + def handle_info(:get_reply, state) do + case Zenohex.Session.get_reply_timeout(state.receiver) do + {:ok, sample} -> + state.callback.(sample) + send(self(), :get_reply) + + {:error, :timeout} -> + send(self(), :get_reply) + + {:error, :disconnected} -> + state.disconnected_cb.() + + {:error, error} -> + Logger.error(inspect(error)) + end + + {:noreply, state} + end +end diff --git a/lib/zenohex/examples/storage.ex b/lib/zenohex/examples/storage.ex new file mode 100644 index 0000000..cead80e --- /dev/null +++ b/lib/zenohex/examples/storage.ex @@ -0,0 +1,28 @@ +defmodule Zenohex.Examples.Storage do + @moduledoc false + + use Supervisor + + alias Zenohex.Examples.Storage + + @doc "Start storage." + def start_link(args \\ %{}) when is_map(args) do + session = Map.get(args, :session) || Zenohex.open!() + key_expr = Map.get(args, :key_expr, "zenohex/examples/**") + Supervisor.start_link(__MODULE__, %{session: session, key_expr: key_expr}, name: __MODULE__) + end + + @doc false + def init(args) when is_map(args) do + children = [ + {Storage.Store, %{}}, + {Storage.Subscriber, args}, + {Storage.Queryable, args} + ] + + Supervisor.init(children, strategy: :one_for_one) + end + + @doc false + def child_spec(args), do: super(args) +end diff --git a/lib/zenohex/examples/storage/queryable.ex b/lib/zenohex/examples/storage/queryable.ex new file mode 100644 index 0000000..88becf2 --- /dev/null +++ b/lib/zenohex/examples/storage/queryable.ex @@ -0,0 +1,46 @@ +defmodule Zenohex.Examples.Storage.Queryable do + @moduledoc false + + use GenServer + + require Logger + + def start_link(args) do + GenServer.start_link(__MODULE__, args, name: __MODULE__) + end + + def init(args) do + {:ok, queryable} = Zenohex.Session.declare_queryable(args.session, args.key_expr) + send(self(), :loop) + {:ok, %{queryable: queryable}} + end + + def handle_info(:loop, state) do + case Zenohex.Queryable.recv_timeout(state.queryable) do + {:ok, query} -> + case store(query) do + {:error, :not_found} -> + nil + + {:ok, samples} -> + Enum.each(samples, &Zenohex.Query.reply(query, &1)) + :ok = Zenohex.Query.finish_reply(query) + # following line is not needed, this is just example of double call + {:error, "ResponseFinal has already been sent"} = Zenohex.Query.finish_reply(query) + end + + {:error, :timeout} -> + nil + + {:error, reason} -> + Logger.error(inspect(reason)) + end + + send(self(), :loop) + {:noreply, state} + end + + defp store(query) when is_struct(query, Zenohex.Query) do + Zenohex.Examples.Storage.Store.get(query.key_expr) + end +end diff --git a/lib/zenohex/examples/storage/store.ex b/lib/zenohex/examples/storage/store.ex new file mode 100644 index 0000000..5c20a18 --- /dev/null +++ b/lib/zenohex/examples/storage/store.ex @@ -0,0 +1,62 @@ +defmodule Zenohex.Examples.Storage.Store do + @moduledoc false + + @behaviour Zenohex.Examples.Storage.StoreBehaviour + + use Agent + + require Logger + + def start_link(initial_state) do + Agent.start_link(fn -> initial_state end, name: __MODULE__) + end + + @impl true + def put(key_expr, sample) do + Agent.update(__MODULE__, fn map -> + Map.put(map, key_expr, sample) + end) + end + + @impl true + def delete(key_expr) do + Agent.update(__MODULE__, fn map -> + find_keys(map, key_expr) + |> Enum.reduce(map, &Map.delete(&2, &1)) + end) + end + + @impl true + def get(key_expr) do + Agent.get( + __MODULE__, + fn map -> + samples = collect_samples(map, key_expr) + + if samples == [] do + {:error, :not_found} + else + {:ok, samples} + end + end + ) + end + + def dump() do + Agent.get(__MODULE__, fn map -> map end) + end + + defp find_keys(map, key_expr) do + Map.keys(map) |> Enum.filter(&Zenohex.KeyExpr.intersects?(&1, key_expr)) + end + + defp collect_samples(map, key_expr) do + find_keys(map, key_expr) + |> Enum.reduce([], fn key, acc -> + case Map.get(map, key) do + nil -> acc + sample -> [sample | acc] + end + end) + end +end diff --git a/lib/zenohex/examples/storage/store_behaviour.ex b/lib/zenohex/examples/storage/store_behaviour.ex new file mode 100644 index 0000000..f0675a5 --- /dev/null +++ b/lib/zenohex/examples/storage/store_behaviour.ex @@ -0,0 +1,10 @@ +defmodule Zenohex.Examples.Storage.StoreBehaviour do + @moduledoc false + + @callback put(key_expr :: String.t(), sample :: Zenohex.Sample.t()) :: + :ok | {:error, reason :: any()} + @callback delete(key_expr :: String.t()) :: + :ok | {:error, reason :: any()} + @callback get(selector :: String.t()) :: + {:ok, [Zenohex.Sample.t()]} | {:error, reason :: any()} +end diff --git a/lib/zenohex/examples/storage/subscriber.ex b/lib/zenohex/examples/storage/subscriber.ex new file mode 100644 index 0000000..68c7022 --- /dev/null +++ b/lib/zenohex/examples/storage/subscriber.ex @@ -0,0 +1,35 @@ +defmodule Zenohex.Examples.Storage.Subscriber do + @moduledoc false + + use GenServer + + require Logger + + def start_link(args) do + GenServer.start_link(__MODULE__, args, name: __MODULE__) + end + + def init(args) do + {:ok, subscriber} = Zenohex.Session.declare_subscriber(args.session, args.key_expr) + send(self(), :loop) + {:ok, %{subscriber: subscriber}} + end + + def handle_info(:loop, state) do + case Zenohex.Subscriber.recv_timeout(state.subscriber) do + {:ok, sample} -> store(sample) + {:error, :timeout} -> nil + {:error, reason} -> Logger.error(inspect(reason)) + end + + send(self(), :loop) + {:noreply, state} + end + + defp store(sample) when is_struct(sample, Zenohex.Sample) do + case sample.kind do + :put -> Zenohex.Examples.Storage.Store.put(sample.key_expr, sample) + :delete -> Zenohex.Examples.Storage.Store.delete(sample.key_expr) + end + end +end diff --git a/lib/zenohex/examples/subscriber.ex b/lib/zenohex/examples/subscriber.ex new file mode 100644 index 0000000..3b19eac --- /dev/null +++ b/lib/zenohex/examples/subscriber.ex @@ -0,0 +1,32 @@ +defmodule Zenohex.Examples.Subscriber do + @moduledoc false + + use Supervisor + + require Logger + + alias Zenohex.Examples.Subscriber + + @doc "Start Subscriber." + def start_link(args \\ %{}) when is_map(args) do + session = Map.get(args, :session) || Zenohex.open!() + key_expr = Map.get(args, :key_expr, "zenohex/examples/**") + callback = Map.get(args, :callback, &Logger.debug(inspect(&1))) + + Supervisor.start_link(__MODULE__, %{session: session, key_expr: key_expr, callback: callback}, + name: __MODULE__ + ) + end + + @doc false + def init(args) when is_map(args) do + children = [ + {Subscriber.Impl, args} + ] + + Supervisor.init(children, strategy: :one_for_one) + end + + @doc false + def child_spec(args), do: super(args) +end diff --git a/lib/zenohex/examples/subscriber/impl.ex b/lib/zenohex/examples/subscriber/impl.ex new file mode 100644 index 0000000..78c43fa --- /dev/null +++ b/lib/zenohex/examples/subscriber/impl.ex @@ -0,0 +1,43 @@ +defmodule Zenohex.Examples.Subscriber.Impl do + @moduledoc false + + use GenServer + + require Logger + + def start_link(args) do + GenServer.start_link(__MODULE__, args, name: __MODULE__) + end + + def init(args) do + session = Map.fetch!(args, :session) + key_expr = Map.fetch!(args, :key_expr) + callback = Map.fetch!(args, :callback) + + {:ok, subscriber} = Zenohex.Session.declare_subscriber(session, key_expr) + state = %{subscriber: subscriber, callback: callback} + + recv_timeout(state) + + {:ok, state} + end + + def handle_info(:loop, state) do + recv_timeout(state) + {:noreply, state} + end + + defp recv_timeout(state) do + case Zenohex.Subscriber.recv_timeout(state.subscriber) do + {:ok, sample} -> + state.callback.(sample) + send(self(), :loop) + + {:error, :timeout} -> + send(self(), :loop) + + {:error, error} -> + Logger.error(inspect(error)) + end + end +end diff --git a/lib/zenohex/keyexpr.ex b/lib/zenohex/keyexpr.ex new file mode 100644 index 0000000..23ccdc6 --- /dev/null +++ b/lib/zenohex/keyexpr.ex @@ -0,0 +1,16 @@ +defmodule Zenohex.KeyExpr do + @moduledoc """ + Documentation for `#{__MODULE__}`. + """ + + alias Zenohex.Nif + + @doc ~S""" + Returns true if the keyexprs intersect. + i.e. there exists at least one key which is contained in both of the sets defined by key_expr1 and key_expr2. + """ + @spec intersects?(String.t(), String.t()) :: boolean() + def intersects?(key_expr1, key_expr2) when is_binary(key_expr1) and is_binary(key_expr2) do + Nif.key_expr_intersects(key_expr1, key_expr2) + end +end diff --git a/lib/zenohex/nif.ex b/lib/zenohex/nif.ex new file mode 100644 index 0000000..de85950 --- /dev/null +++ b/lib/zenohex/nif.ex @@ -0,0 +1,71 @@ +defmodule Zenohex.Nif do + @moduledoc false + + mix_config = Mix.Project.config() + version = mix_config[:version] + github_url = mix_config[:package][:links]["GitHub"] + + use RustlerPrecompiled, + otp_app: :zenohex, + crate: "zenohex_nif", + version: version, + base_url: "#{github_url}/releases/download/v#{version}", + targets: + RustlerPrecompiled.Config.default_targets() + |> Enum.reject(&(&1 == "riscv64gc-unknown-linux-gnu")) + + # for Nerves + @compile {:autoload, false} + + alias Zenohex.Publisher + alias Zenohex.Subscriber + alias Zenohex.Queryable + alias Zenohex.Query + alias Zenohex.Config + + defp err(), do: :erlang.nif_error(:nif_not_loaded) + + def zenoh_open(_config \\ %Config{}), do: err() + + for type <- ["integer", "float", "binary"] do + def unquote(:"session_put_#{type}")(_session, _key_expr, _value), do: err() + end + + def session_get_reply_receiver(_session, _selector, _opts \\ %Query.Options{}), do: err() + + def session_get_reply_timeout(_receiver, _timeout_us), do: err() + + def session_delete(_session, _key_expr), do: err() + + def declare_publisher(_session, _key_expr, _opts \\ %Publisher.Options{}), do: err() + + for type <- ["integer", "float", "binary"] do + def unquote(:"publisher_put_#{type}")(_publisher, _value), do: err() + end + + def publisher_delete(_publisher), do: err() + + def publisher_congestion_control(_publisher, _congestion_control), do: err() + + def publisher_priority(_publisher, _priority), do: err() + + def declare_subscriber(_session, _key_expr, _opts \\ %Subscriber.Options{}), do: err() + + def subscriber_recv_timeout(_subscriber, _timeout_us), do: err() + + def declare_pull_subscriber(_session, _key_expr, _opts \\ %Subscriber.Options{}), do: err() + + def pull_subscriber_pull(_pull_subscriber), do: err() + + def pull_subscriber_recv_timeout(_pull_subscriber, _timeout_us), do: err() + + def declare_queryable(_session, _key_expr, _opts \\ %Queryable.Options{}), do: err() + + def queryable_recv_timeout(_queryable, _timeout_us), do: err() + + def query_reply(_query, _sample), do: err() + + def query_finish_reply(_query), do: err() + + def key_expr_intersects(_key_expr1, _key_expr2), do: err() +end diff --git a/lib/zenohex/publisher.ex b/lib/zenohex/publisher.ex new file mode 100644 index 0000000..b819e75 --- /dev/null +++ b/lib/zenohex/publisher.ex @@ -0,0 +1,96 @@ +defmodule Zenohex.Publisher do + @moduledoc """ + Documentation for `#{__MODULE__}`. + """ + + alias Zenohex.Nif + + @type t :: reference() + + defmodule Options do + @moduledoc """ + Documentation for `#{__MODULE__}`. + + Used by `Zenohex.Session.declare_publisher/3`. + """ + + @type t :: %__MODULE__{congestion_control: congestion_control(), priority: priority()} + @type congestion_control :: :drop | :block + @type priority :: + :real_time + | :interactive_high + | :interactive_low + | :data_high + | :data + | :data_low + | :background + + defstruct congestion_control: :drop, priority: :data + end + + @doc ~S""" + Put data. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> {:ok, publisher} = Zenohex.Session.declare_publisher(session, "key/expression") + iex> :ok = Zenohex.Publisher.put(publisher, "value") + iex> :ok = Zenohex.Publisher.put(publisher, 0) + iex> :ok = Zenohex.Publisher.put(publisher, 0.0) + """ + @spec put(t(), binary() | integer() | float()) :: :ok | {:error, reason :: any()} + def put(publisher, value) when is_binary(value) do + Nif.publisher_put_binary(publisher, value) + end + + def put(publisher, value) when is_integer(value) do + Nif.publisher_put_integer(publisher, value) + end + + def put(publisher, value) when is_float(value) do + Nif.publisher_put_float(publisher, value) + end + + @doc ~S""" + Delete data. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> {:ok, publisher} = Zenohex.Session.declare_publisher(session, "key/expression") + iex> :ok = Zenohex.Publisher.delete(publisher) + """ + @spec delete(t()) :: :ok | {:error, reason :: any()} + def delete(publisher) do + Nif.publisher_delete(publisher) + end + + @doc ~S""" + Change the congestion_control to apply when routing the data. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> {:ok, publisher} = Zenohex.Session.declare_publisher(session, "key/expression") + iex> Zenohex.Publisher.congestion_control(publisher, :drop) + """ + @spec congestion_control(t(), Options.congestion_control()) :: t() + def congestion_control(publisher, congestion_control) do + Nif.publisher_congestion_control(publisher, congestion_control) + end + + @doc ~S""" + Change the priority of the written data. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> {:ok, publisher} = Zenohex.Session.declare_publisher(session, "key/expression") + iex> Zenohex.Publisher.priority(publisher, :real_time) + """ + @spec priority(t(), Options.priority()) :: t() + def priority(publisher, priority) do + Nif.publisher_priority(publisher, priority) + end +end diff --git a/lib/zenohex/pull_subscriber.ex b/lib/zenohex/pull_subscriber.ex new file mode 100644 index 0000000..4992aee --- /dev/null +++ b/lib/zenohex/pull_subscriber.ex @@ -0,0 +1,45 @@ +defmodule Zenohex.PullSubscriber do + @moduledoc """ + Documentation for `#{__MODULE__}`. + """ + + alias Zenohex.Nif + alias Zenohex.Sample + + @type t :: reference() + + @doc """ + Pull data. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> {:ok, pull_subscriber} = Zenohex.Session.declare_pull_subscriber(session, "key/expression") + iex> Zenohex.PullSubscriber.pull(pull_subscriber) + :ok + """ + @spec pull(t()) :: :ok | {:error, reason :: any()} + def pull(pull_subscriber) when is_reference(pull_subscriber) do + Nif.pull_subscriber_pull(pull_subscriber) + end + + @doc """ + Receive data. + Normally users don't need to change the default timeout_us. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> {:ok, pull_subscriber} = Zenohex.Session.declare_pull_subscriber(session, "key/expression") + iex> Zenohex.PullSubscriber.recv_timeout(pull_subscriber) + {:error, :timeout} + """ + @spec recv_timeout(t(), pos_integer()) :: + {:ok, Sample.t()} + | {:error, :timeout} + | {:error, reason :: any()} + def recv_timeout(pull_subscriber, timeout_us \\ 1000) + when is_reference(pull_subscriber) and is_integer(timeout_us) and timeout_us > 0 do + Nif.pull_subscriber_recv_timeout(pull_subscriber, timeout_us) + end +end diff --git a/lib/zenohex/query.ex b/lib/zenohex/query.ex new file mode 100644 index 0000000..53100f4 --- /dev/null +++ b/lib/zenohex/query.ex @@ -0,0 +1,53 @@ +defmodule Zenohex.Query do + @moduledoc """ + Documentation for `#{__MODULE__}`. + + Structs received by a `Zenohex.Queryable.recv_timeout/1`. + """ + + alias Zenohex.Nif + alias Zenohex.Sample + + @type t :: %__MODULE__{ + key_expr: String.t(), + parameters: String.t(), + value: binary() | integer() | float() | :undefined, + reference: reference() + } + defstruct [:key_expr, :parameters, :value, :reference] + + defmodule Options do + @moduledoc """ + Documentation for `#{__MODULE__}`. + + Used by `Zenohex.Session.get_timeout/4` and `Zenohex.Session.get_reply_receiver/3`. + """ + + @type t :: %__MODULE__{target: target(), consolidation: consolidation()} + @type target :: :best_matching | :all | :all_complete + @type consolidation :: :auto | :none | :monotonic | :latest + defstruct target: :best_matching, consolidation: :auto + end + + @doc ~S""" + Sends a reply to this Query. User can call `reply/2` multiple times to send multiple samples. + + > ### Warning {: .warning} + > Do not forget to call `finish_reply/1` to finish the reply. + """ + @spec reply(t(), Sample.t()) :: :ok | {:error, reason :: any()} + def reply(query, sample) when is_struct(query, __MODULE__) and is_struct(sample, Sample) do + Nif.query_reply(query, sample) + end + + @doc ~S""" + Finish reply. + + > ### Warning {: .warning} + > `finish_reply/1` must be called after `reply/2`. + """ + @spec finish_reply(t()) :: :ok | {:error, reason :: any()} + def finish_reply(query) when is_struct(query, __MODULE__) do + Nif.query_finish_reply(query) + end +end diff --git a/lib/zenohex/queryable.ex b/lib/zenohex/queryable.ex new file mode 100644 index 0000000..5ff7bc1 --- /dev/null +++ b/lib/zenohex/queryable.ex @@ -0,0 +1,42 @@ +defmodule Zenohex.Queryable do + @moduledoc """ + Documentation for `#{__MODULE__}`. + """ + + alias Zenohex.Nif + alias Zenohex.Query + + @type t :: reference() + + defmodule Options do + @moduledoc """ + Documentation for `#{__MODULE__}`. + + Used by `Zenohex.Session.declare_queryable/3`. + """ + + @type t :: %__MODULE__{complete: complete()} + @type complete :: boolean() + defstruct complete: false + end + + @doc """ + Receive query. + Normally users don't need to change the default timeout_us. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> {:ok, queryable} = Zenohex.Session.declare_queryable(session, "key/expression") + iex> Zenohex.Queryable.recv_timeout(queryable) + {:error, :timeout} + """ + @spec recv_timeout(t(), pos_integer()) :: + {:ok, Query.t()} + | {:error, :timeout} + | {:error, reason :: any()} + def recv_timeout(queryable, timeout_us \\ 1000) + when is_reference(queryable) and is_integer(timeout_us) and timeout_us > 0 do + Nif.queryable_recv_timeout(queryable, timeout_us) + end +end diff --git a/lib/zenohex/sample.ex b/lib/zenohex/sample.ex new file mode 100644 index 0000000..81a8dde --- /dev/null +++ b/lib/zenohex/sample.ex @@ -0,0 +1,19 @@ +defmodule Zenohex.Sample do + @moduledoc """ + Documentation for `#{__MODULE__}`. + + Structs received by + + * `Zenohex.Session.get_timeout/3` and `Zenohex.Session.get_reply_timeout/1` + * `Zenohex.Subscriber.recv_timeout/1` + * `Zenohex.PullSubscriber.recv_timeout/1` + """ + + @type t :: %__MODULE__{ + key_expr: String.t(), + value: binary() | integer() | float(), + kind: :put | :delete, + reference: reference() | :undefined + } + defstruct key_expr: "", value: "", kind: :put, reference: :undefined +end diff --git a/lib/zenohex/session.ex b/lib/zenohex/session.ex new file mode 100644 index 0000000..d596483 --- /dev/null +++ b/lib/zenohex/session.ex @@ -0,0 +1,175 @@ +defmodule Zenohex.Session do + @moduledoc """ + Documentation for `#{__MODULE__}`. + """ + + alias Zenohex.Nif + alias Zenohex.Publisher + alias Zenohex.Subscriber + alias Zenohex.PullSubscriber + alias Zenohex.Queryable + alias Zenohex.Query + alias Zenohex.Sample + + @type t :: reference() + @type receiver :: reference() + + @doc ~S""" + Create a Publisher for the given key expression. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> Zenohex.Session.declare_publisher(session, "key/expression") + """ + @spec declare_publisher(t(), String.t(), Publisher.Options.t()) :: + {:ok, Publisher.t()} | {:error, reason :: any()} + def declare_publisher(session, key_expr, opts \\ %Publisher.Options{}) + when is_reference(session) and is_binary(key_expr) and is_struct(opts, Publisher.Options) do + Nif.declare_publisher(session, key_expr, opts) + end + + @doc ~S""" + Create a Subscriber for the given key expression. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> Zenohex.Session.declare_subscriber(session, "key/expression") + """ + @spec declare_subscriber(t(), String.t(), Subscriber.Options.t()) :: + {:ok, Subscriber.t()} | {:error, reason :: any()} + def declare_subscriber(session, key_expr, opts \\ %Subscriber.Options{}) + when is_reference(session) and is_binary(key_expr) and is_struct(opts, Subscriber.Options) do + Nif.declare_subscriber(session, key_expr, opts) + end + + @doc ~S""" + Create a PullSubscriber for the given key expression. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> Zenohex.Session.declare_pull_subscriber(session, "key/expression") + """ + @spec declare_pull_subscriber(t(), String.t(), Subscriber.Options.t()) :: + {:ok, PullSubscriber.t()} | {:error, reason :: any()} + def declare_pull_subscriber(session, key_expr, opts \\ %Subscriber.Options{}) + when is_reference(session) and is_binary(key_expr) and is_struct(opts, Subscriber.Options) do + Nif.declare_pull_subscriber(session, key_expr, opts) + end + + @doc ~S""" + Create a Quaryable for the given key expression. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> Zenohex.Session.declare_queryable(session, "key/expression") + """ + @spec declare_queryable(t(), String.t(), Queryable.Options.t()) :: + {:ok, Queryable.t()} | {:error, reason :: any()} + def declare_queryable(session, key_expr, opts \\ %Queryable.Options{}) + when is_reference(session) and is_binary(key_expr) and is_struct(opts, Queryable.Options) do + Nif.declare_queryable(session, key_expr, opts) + end + + @doc ~S""" + Put data. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> :ok = Zenohex.Session.put(session, "key/expression", "value") + iex> :ok = Zenohex.Session.put(session, "key/expression", 0) + iex> :ok = Zenohex.Session.put(session, "key/expression", 0.0) + """ + @spec put(t(), String.t(), binary() | integer() | float()) :: + :ok | {:error, reason :: any()} + def put(session, key_expr, value) + when is_reference(session) and is_binary(key_expr) and is_binary(value) do + Nif.session_put_binary(session, key_expr, value) + end + + def put(session, key_expr, value) + when is_reference(session) and is_binary(key_expr) and is_integer(value) do + Nif.session_put_integer(session, key_expr, value) + end + + def put(session, key_expr, value) + when is_reference(session) and is_binary(key_expr) and is_float(value) do + Nif.session_put_float(session, key_expr, value) + end + + @doc ~S""" + Get one data from the matching queryables in the system. + To get multiple data, use `get_reply_receiver/2` and `get_reply_timeout/1` instead. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> Zenohex.Session.get_timeout(session, "key/expression", 1000) + {:error, :disconnected} + """ + @spec get_timeout(t(), String.t(), pos_integer(), Query.Options.t()) :: + {:ok, Sample.t()} + | {:error, :timeout} + | {:error, :disconnected} + | {:error, reason :: any()} + def get_timeout(session, selector, timeout_us, opts \\ %Query.Options{}) do + case get_reply_receiver(session, selector, opts) do + {:ok, receiver} -> get_reply_timeout(receiver, timeout_us) + error -> error + end + end + + @doc ~S""" + Get reply receiver from the matching queryables in the system. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> Zenohex.Session.get_reply_receiver(session, "key/**") + """ + @spec get_reply_receiver(t(), String.t(), Query.Options.t()) :: + {:ok, receiver()} | {:error, reason :: any()} + def get_reply_receiver(session, selector, opts \\ %Query.Options{}) + when is_reference(session) and is_binary(selector) and is_struct(opts, Query.Options) do + Nif.session_get_reply_receiver(session, selector, opts) + end + + @doc ~S""" + Query data from receiver. + Normally users don't need to change the default timeout_us. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> {:ok, receiver} = Zenohex.Session.get_reply_receiver(session, "key/**") + iex> Zenohex.Session.get_reply_timeout(receiver) + {:error, :disconnected} + """ + @spec get_reply_timeout(receiver(), pos_integer()) :: + {:ok, Sample.t()} + | {:error, :timeout} + | {:error, :disconnected} + | {:error, reason :: any()} + def get_reply_timeout(receiver, timeout_us \\ 1000) + when is_reference(receiver) and is_integer(timeout_us) and timeout_us > 0 do + Nif.session_get_reply_timeout(receiver, timeout_us) + end + + @doc ~S""" + Delete data. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> Zenohex.Session.delete(session, "key/expression") + :ok + """ + @spec delete(t(), String.t()) :: :ok | {:error, reason :: any()} + def delete(session, key_expr) when is_reference(session) and is_binary(key_expr) do + Nif.session_delete(session, key_expr) + end +end diff --git a/lib/zenohex/subscriber.ex b/lib/zenohex/subscriber.ex new file mode 100644 index 0000000..a10c0ee --- /dev/null +++ b/lib/zenohex/subscriber.ex @@ -0,0 +1,42 @@ +defmodule Zenohex.Subscriber do + @moduledoc """ + Documentation for `#{__MODULE__}`. + """ + + alias Zenohex.Nif + alias Zenohex.Sample + + @opaque t :: reference() + + defmodule Options do + @moduledoc """ + Documentation for `#{__MODULE__}`. + + Used by `Zenohex.Session.declare_subscriber/3` and `Zenohex.Session.declare_pull_subscriber/3`. + """ + + @type t :: %__MODULE__{reliability: reliability()} + @type reliability :: :best_effort | :reliable + defstruct reliability: :best_effort + end + + @doc """ + Receive data. + Normally users don't need to change the default timeout_us. + + ## Examples + + iex> {:ok, session} = Zenohex.open() + iex> {:ok, subscriber} = Zenohex.Session.declare_subscriber(session, "key/expression") + iex> Zenohex.Subscriber.recv_timeout(subscriber) + {:error, :timeout} + """ + @spec recv_timeout(t(), pos_integer()) :: + {:ok, Sample.t()} + | {:error, :timeout} + | {:error, reason :: any()} + def recv_timeout(subscriber, timeout_us \\ 1000) + when is_reference(subscriber) and is_integer(timeout_us) and timeout_us > 0 do + Nif.subscriber_recv_timeout(subscriber, timeout_us) + end +end diff --git a/mix.exs b/mix.exs index ccc4922..0f8c292 100644 --- a/mix.exs +++ b/mix.exs @@ -1,6 +1,8 @@ defmodule Zenohex.MixProject do use Mix.Project - @version "0.1.5" + + @version "0.2.0-rc.2" + @source_url "https://github.com/b5g-ex/zenohex" def project do [ @@ -10,7 +12,18 @@ defmodule Zenohex.MixProject do description: "Zenoh client library for elixir.", package: package(), start_permanent: Mix.env() == :prod, - deps: deps() + deps: deps(), + elixirc_paths: elixirc_paths(Mix.env()), + # Docs + name: "Zenohex", + source_url: @source_url, + docs: docs(), + test_coverage: test_coverage(), + dialyzer: dialyzer(), + aliases: [ + {:test, [&suggest/1, "test"]}, + {:"test.watch", [&suggest/1, "test.watch"]} + ] ] end @@ -21,12 +34,19 @@ defmodule Zenohex.MixProject do ] end + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] + # Run "mix help deps" to learn about dependencies. defp deps do [ {:rustler_precompiled, "~> 0.7.1"}, - {:rustler, ">= 0.30.0", optional: true}, - {:ex_doc, "~> 0.31.0", only: :dev} + {:rustler, ">= 0.31.0", optional: true}, + {:ex_doc, "~> 0.31.0", only: :dev}, + {:mix_test_watch, "~> 1.0", only: [:dev, :test], runtime: false}, + {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, + {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}, + {:toml, "~> 0.7", runtime: false} ] end @@ -34,16 +54,78 @@ defmodule Zenohex.MixProject do [ name: "zenohex", files: [ - "lib", - "native/nifzenoh/.cargo", - "native/nifzenoh/src", - "native/nifzenoh/Cargo*", + "lib/zenohex.ex", + "lib/zenohex/config", + "lib/zenohex/*.ex", + "native/zenohex_nif/.cargo", + "native/zenohex_nif/src", + "native/zenohex_nif/Cargo*", + "LICENSE", + "README.md", "checksum-*.exs", "mix.exs" ], maintainers: ["s-hosoai"], licenses: ["MIT"], - links: %{"GitHub" => "https://github.com/b5g-ex/zenohex"} + links: %{"GitHub" => @source_url} ] end + + defp docs() do + [ + extras: ["README.md", "LICENSE"], + main: "readme", + groups_for_modules: [ + Configs: [ + Zenohex.Config, + Zenohex.Config.Connect, + Zenohex.Config.Scouting + ], + Options: [ + Zenohex.Publisher.Options, + Zenohex.Query.Options, + Zenohex.Queryable.Options, + Zenohex.Subscriber.Options + ] + ] + ] + end + + defp test_coverage() do + [ + ignore_modules: [Zenohex.Nif] + ] + end + + defp dialyzer() do + [ + plt_file: {:no_warn, "priv/plts/project.plt"}, + plt_core_path: "priv/plts/core.plt" + ] + end + + defp suggest(_args) do + if is_nil(System.get_env("API_OPEN_SESSION_DELAY")) do + """ + ==================================================================== + HEY, ZENOHEX DEVELOPER. IF YOU WANNA REDUCE TEST TIME, DO FOLLOWINGS + export API_OPEN_SESSION_DELAY=0 && mix compile --force + ==================================================================== + """ + |> String.trim_trailing() + |> Mix.shell().info() + end + + if is_nil(System.get_env("SCOUTING_DELAY")) do + """ + ==================================================================== + HEY, ZENOHEX DEVELOPER. IF YOU WANNA REDUCE TEST TIME, + YOU CAN ADJUST SCOUTING DELAY, LIKE FOLLOWINGS + SCOUTING_DELAY=30 mix test + ==================================================================== + """ + |> String.trim_trailing() + |> Mix.shell().info() + end + end end diff --git a/mix.lock b/mix.lock index d713c3c..2fdc411 100644 --- a/mix.lock +++ b/mix.lock @@ -1,13 +1,19 @@ %{ + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"}, + "credo": {:hex, :credo, "1.7.3", "05bb11eaf2f2b8db370ecaa6a6bda2ec49b2acd5e0418bc106b73b07128c0436", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "35ea675a094c934c22fb1dca3696f3c31f2728ae6ef5a53b5d648c11180a4535"}, + "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, + "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"}, + "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.3", "d684f4bac8690e70b06eb52dad65d26de2eefa44cd19d64a8095e1417df7c8fd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "b78dc853d2e670ff6390b605d807263bf606da3c82be37f9d7f68635bd886fc9"}, + "mix_test_watch": {:hex, :mix_test_watch, "1.1.1", "eee6fc570d77ad6851c7bc08de420a47fd1e449ef5ccfa6a77ef68b72e7e51ad", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "f82262b54dee533467021723892e15c3267349849f1f737526523ecba4e6baae"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "rustler": {:hex, :rustler, "0.30.0", "cefc49922132b072853fa9b0ca4dc2ffcb452f68fb73b779042b02d545e097fb", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "9ef1abb6a7dda35c47cfc649e6a5a61663af6cf842a55814a554a84607dee389"}, + "rustler": {:hex, :rustler, "0.31.0", "7e5eefe61e6e6f8901e5aa3de60073d360c6320d9ec363027b0197297b80c46a", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "99e378459bfb9c3bda6d3548b2b3bc6f9ad97f728f76bdbae7bf5c770a4f8abd"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.7.1", "ecadf02cc59a0eccbaed6c1937303a5827fbcf60010c541595e6d3747d3d0f9f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "b9e4657b99a1483ea31502e1d58c464bedebe9028808eda45c3a429af4550c66"}, "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, } diff --git a/native/nifzenoh/Cargo.toml b/native/nifzenoh/Cargo.toml deleted file mode 100644 index 8dae30d..0000000 --- a/native/nifzenoh/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "nifzenoh" -version = "0.1.5" -authors = [] -edition = "2018" - -[lib] -name = "nifzenoh" -path = "src/lib.rs" -crate-type = ["cdylib"] - -[dependencies] -rustler = "0.30.0" -zenoh = { version = "0.10.1-rc", default-features = true } -async-std = { version = "=1.12.0", default-features = false, features = [ - "attributes", -] } -env_logger = "0.10.1" -clap = "4.4.16" -futures = "0.3.30" -flume = "0.11.0" diff --git a/native/nifzenoh/Cross.toml b/native/nifzenoh/Cross.toml deleted file mode 100644 index ea92ea7..0000000 --- a/native/nifzenoh/Cross.toml +++ /dev/null @@ -1,4 +0,0 @@ -[build.env] -passthrough = [ - "RUSTLER_NIF_VERSION" -] \ No newline at end of file diff --git a/native/nifzenoh/src/lib.rs b/native/nifzenoh/src/lib.rs deleted file mode 100644 index 860576a..0000000 --- a/native/nifzenoh/src/lib.rs +++ /dev/null @@ -1,119 +0,0 @@ -use futures::executor::block_on; -use rustler::types::atom::ok; -use rustler::types::{Encoder, Pid}; -use rustler::{Env, OwnedEnv, ResourceArc, Term}; -use std::sync::Mutex; -use zenoh::config::Config; -use zenoh::prelude::r#async::*; -use zenoh::publication::Publisher; -pub mod tester; -use tester::tester::{tester_pub, tester_sub}; - -struct SessionContainer { - session_mux: Mutex<&'static Session>, -} - -struct PublisherContainer { - publisher_mux: Mutex>, -} - -fn load<'a>(env: Env<'a>, _: Term<'a>) -> bool { - rustler::resource!(SessionContainer, env); - rustler::resource!(PublisherContainer, env); - true -} - -#[rustler::nif] -fn zenoh_open<'a>() -> ResourceArc { - ResourceArc::new(SessionContainer { - session_mux: Mutex::new(Session::leak( - block_on(zenoh::open(Config::default()).res()).unwrap(), - )), - }) -} - -#[rustler::nif] -fn session_declare_publisher<'a>( - env: Env<'a>, - resource_session: ResourceArc, - keyexpr: String, -) -> Term<'a> { - let session = resource_session.session_mux.lock().unwrap(); - let publisher = session.declare_publisher(keyexpr); - let resource_publisher = ResourceArc::new(PublisherContainer { - publisher_mux: Mutex::new(block_on(publisher.res()).unwrap()), - }); - (ok(), resource_publisher).encode(env) -} - -fn publisher_put<'a>( - env: Env<'a>, - resource_session: ResourceArc, - value: Value, -) -> Term<'a> { - let publisher = &resource_session.publisher_mux.lock().unwrap(); - block_on(publisher.put(value).res()).unwrap(); - (ok()).encode(env) -} - -#[rustler::nif] -fn publisher_put_string<'a>( - env: Env<'a>, - resource_session: ResourceArc, - value: String, -) -> Term<'a> { - publisher_put(env, resource_session, Value::from(value)) -} - -#[rustler::nif] -fn publisher_put_integer<'a>( - env: Env<'a>, - resource_session: ResourceArc, - value: i64, -) -> Term<'a> { - publisher_put(env, resource_session, Value::from(value)) -} - -#[rustler::nif] -fn publisher_put_float<'a>( - env: Env<'a>, - resource_session: ResourceArc, - value: f64, -) -> Term<'a> { - publisher_put(env, resource_session, Value::from(value)) -} - -#[rustler::nif] -fn session_declare_subscriber<'a>( - env: Env<'a>, - resource_session: ResourceArc, - keyexpr: String, - pid: Pid, -) -> Term<'a> { - let mut subscriber_env = OwnedEnv::new(); - - let session = resource_session.session_mux.lock().unwrap(); - let subscriber = block_on(session.declare_subscriber(keyexpr).res()).unwrap(); - - std::thread::spawn(move || loop { - let sample = block_on(subscriber.recv_async()).unwrap(); - subscriber_env.send_and_clear(&pid, |env| sample.value.to_string().encode(env)); - }); - - ok().encode(env) -} - -rustler::init!( - "Elixir.NifZenoh", - [ - zenoh_open, - session_declare_publisher, - publisher_put_string, - publisher_put_float, - publisher_put_integer, - tester_pub, - tester_sub, - session_declare_subscriber, - ], - load = load -); diff --git a/native/nifzenoh/src/tester.rs b/native/nifzenoh/src/tester.rs deleted file mode 100644 index cc0ee89..0000000 --- a/native/nifzenoh/src/tester.rs +++ /dev/null @@ -1,63 +0,0 @@ -pub mod tester { - use async_std::task::sleep; - use futures::executor::block_on; - use futures::prelude::*; - use futures::select; - use std::time::Duration; - use zenoh::config::Config; - use zenoh::prelude::r#async::*; - - pub async fn pub_zenoh(key_expr: String, value: String) { - env_logger::init(); - - let session = zenoh::open(Config::default()).res().await.unwrap(); - println!("Declaring Publisher on '{}'...", key_expr); - let publisher = session.declare_publisher(&key_expr).res().await.unwrap(); - - for idx in 0..u32::MAX { - sleep(Duration::from_secs(1)).await; - let buf = format!("[{:4}] {}", idx, value); - println!("Putting Data ('{}': '{}')...", &key_expr, buf); - publisher.put(buf).res().await.unwrap(); - } - } - - pub async fn sub_zenoh(key_expr: String) { - env_logger::init(); - - println!("Opening session..."); - let session = zenoh::open(Config::default()).res().await.unwrap(); - - println!("Declaring Subscriber on '{}'...", &key_expr); - let subscriber = session.declare_subscriber(&key_expr).res().await.unwrap(); - - println!("Enter 'q' to quit..."); - let mut stdin = async_std::io::stdin(); - let mut input = [0_u8]; - loop { - select!( - sample = subscriber.recv_async() => { - let sample = sample.unwrap(); - println!(">> [Subscriber] Received {} ('{}': '{}')", - sample.kind, sample.key_expr.as_str(), sample.value); - }, - - _ = stdin.read_exact(&mut input).fuse() => { - match input[0] { - b'q' => break, - 0 => sleep(Duration::from_secs(1)).await, - _ => (), - } - } - ); - } - } - #[rustler::nif] - fn tester_pub(key_expr: String, value: String) { - block_on(pub_zenoh(key_expr, value)); - } - #[rustler::nif] - pub fn tester_sub(key_exprt: String) { - block_on(sub_zenoh(key_exprt)); - } -} diff --git a/native/nifzenoh/.cargo/config.toml b/native/zenohex_nif/.cargo/config.toml similarity index 65% rename from native/nifzenoh/.cargo/config.toml rename to native/zenohex_nif/.cargo/config.toml index b5d186c..6a4c852 100644 --- a/native/nifzenoh/.cargo/config.toml +++ b/native/zenohex_nif/.cargo/config.toml @@ -4,11 +4,17 @@ rustflags = [ "-C", "link-arg=dynamic_lookup", ] +# See https://github.com/rust-lang/rust/issues/59302 [target.x86_64-unknown-linux-musl] rustflags = [ "-C", "target-feature=-crt-static" ] +[target.aarch64-unknown-linux-musl] +rustflags = [ + "-C", "target-feature=-crt-static" +] + # Provides a small build size, but takes more time to build. [profile.release] -lto = true \ No newline at end of file +lto = true diff --git a/native/nifzenoh/.gitignore b/native/zenohex_nif/.gitignore similarity index 100% rename from native/nifzenoh/.gitignore rename to native/zenohex_nif/.gitignore diff --git a/native/nifzenoh/Cargo.lock b/native/zenohex_nif/Cargo.lock similarity index 82% rename from native/nifzenoh/Cargo.lock rename to native/zenohex_nif/Cargo.lock index 2d23e88..c658e0f 100644 --- a/native/nifzenoh/Cargo.lock +++ b/native/zenohex_nif/Cargo.lock @@ -30,20 +30,21 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -56,9 +57,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "anstream" -version = "0.6.7" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -76,20 +77,20 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -104,9 +105,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "array-init" @@ -135,32 +136,45 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-channel" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +dependencies = [ + "concurrent-queue", + "event-listener 4.0.3", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-executor" -version = "1.5.4" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1da3ae8dabd9c00f453a329dfe1fb28da3c0a72e2478cdcd93171740c20499" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ - "async-lock", + "async-lock 3.3.0", "async-task", "concurrent-queue", "fastrand 2.0.1", - "futures-lite", + "futures-lite 2.2.0", "slab", ] [[package]] name = "async-global-executor" -version = "2.3.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel", + "async-channel 2.1.1", "async-executor", - "async-io", - "async-lock", + "async-io 2.3.0", + "async-lock 3.3.0", "blocking", - "futures-lite", + "futures-lite 2.2.0", "once_cell", "tokio", ] @@ -171,20 +185,39 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "async-lock", + "async-lock 2.8.0", "autocfg", "cfg-if", "concurrent-queue", - "futures-lite", + "futures-lite 1.13.0", "log", "parking", - "polling", - "rustix 0.37.23", + "polling 2.8.0", + "rustix 0.37.27", "slab", - "socket2 0.4.9", + "socket2 0.4.10", "waker-fn", ] +[[package]] +name = "async-io" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb41eb19024a91746eba0773aa5e16036045bbf45733766661099e182ea6a744" +dependencies = [ + "async-lock 3.3.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.2.0", + "parking", + "polling 3.3.2", + "rustix 0.38.30", + "slab", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "async-lock" version = "2.8.0" @@ -194,28 +227,39 @@ dependencies = [ "event-listener 2.5.3", ] +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-process" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf012553ce51eb7aa6dc2143804cc8252bd1cb681a1c5cb7fa94ca88682dee1d" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" dependencies = [ - "async-io", - "async-lock", + "async-io 1.13.0", + "async-lock 2.8.0", "async-signal", "blocking", "cfg-if", - "event-listener 3.0.0", - "futures-lite", - "rustix 0.38.14", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.30", "windows-sys 0.48.0", ] [[package]] name = "async-rustls" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd10f063fb367d26334e10c50c67ea31ac542b8c3402be2251db4cfc5d74ba66" +checksum = "ecfa55659849ace733f86ccd219da40abd8bc14124e40b312433e85a5a266e77" dependencies = [ "futures-io", "rustls", @@ -223,18 +267,17 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99f3cb3f9ff89f7d718fbb942c9eb91bedff12e396adf09a622dfe7ffec2bc2" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" dependencies = [ - "async-io", - "async-lock", + "async-io 2.3.0", + "async-lock 2.8.0", "atomic-waker", "cfg-if", - "concurrent-queue", "futures-core", "futures-io", - "libc", + "rustix 0.38.30", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -247,16 +290,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-attributes", - "async-channel", + "async-channel 1.9.0", "async-global-executor", - "async-io", - "async-lock", + "async-io 1.13.0", + "async-lock 2.8.0", "async-process", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite", + "futures-lite 1.13.0", "gloo-timers", "kv-log-macro", "log", @@ -270,19 +313,19 @@ dependencies = [ [[package]] name = "async-task" -version = "4.4.1" +version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9441c6b2fe128a7c2bf680a44c34d0df31ce09e5b7e401fcca3faa483dbc921" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -314,9 +357,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.4" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -332,9 +375,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "block-buffer" @@ -347,16 +390,16 @@ dependencies = [ [[package]] name = "blocking" -version = "1.4.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c4ef1f913d78636d78d538eec1f18de81e481f44b1be0a81060090530846e1" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel", - "async-lock", + "async-channel 2.1.1", + "async-lock 3.3.0", "async-task", "fastrand 2.0.1", "futures-io", - "futures-lite", + "futures-lite 2.2.0", "piper", "tracing", ] @@ -369,9 +412,9 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -412,9 +455,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.16" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" dependencies = [ "clap_builder", "clap_derive", @@ -422,9 +465,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.16" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" dependencies = [ "anstream", "anstyle", @@ -441,7 +484,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -458,33 +501,33 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "concurrent-queue" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_format" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ "proc-macro2", "quote", @@ -493,9 +536,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -503,27 +546,24 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-common" @@ -537,9 +577,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "der" @@ -587,15 +627,15 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" [[package]] name = "env_logger" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -612,23 +652,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -639,9 +668,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e56284f00d94c1bc7fd3c77027b4623c88c1f53d8d2394c6199f2921dea325" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" dependencies = [ "concurrent-queue", "parking", @@ -659,6 +688,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -700,9 +739,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -770,6 +809,19 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-lite" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +dependencies = [ + "fastrand 2.0.1", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.30" @@ -778,7 +830,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -823,9 +875,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "js-sys", @@ -836,30 +888,28 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "git-version" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b0decc02f4636b9ccad390dcbe77b722a77efedfa393caf8379a51d5c61899" +checksum = "1ad568aa3db0fcbc81f2f116137f263d7304f512a1209b35b85150d3ef88ad19" dependencies = [ "git-version-macro", - "proc-macro-hack", ] [[package]] name = "git-version-macro" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f" +checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ - "proc-macro-hack", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -891,9 +941,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -907,9 +957,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" [[package]] name = "hex" @@ -928,18 +978,18 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -960,9 +1010,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -970,12 +1020,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.3", ] [[package]] @@ -1018,26 +1068,26 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ "hermit-abi", - "rustix 0.38.14", - "windows-sys 0.48.0", + "rustix 0.38.30", + "windows-sys 0.52.0", ] [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] @@ -1055,9 +1105,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] @@ -1091,15 +1141,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.148" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libloading" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", "windows-sys 0.48.0", @@ -1107,9 +1157,20 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall", +] [[package]] name = "linux-raw-sys" @@ -1119,15 +1180,15 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1144,18 +1205,18 @@ dependencies = [ [[package]] name = "lz4_flex" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea9b256699eda7b0387ffbc776dd625e28bde3918446381781245b7a50349d8" +checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15" dependencies = [ "twox-hash", ] [[package]] name = "memchr" -version = "2.6.3" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "miniz_oxide" @@ -1168,9 +1229,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", @@ -1186,26 +1247,13 @@ dependencies = [ "getrandom", ] -[[package]] -name = "nifzenoh" -version = "0.1.5" -dependencies = [ - "async-std", - "clap", - "env_logger", - "flume", - "futures", - "rustler", - "zenoh", -] - [[package]] name = "nix" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.2", "cfg-if", "libc", ] @@ -1256,9 +1304,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", @@ -1276,18 +1324,18 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl-probe" @@ -1312,9 +1360,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "paste" @@ -1333,15 +1381,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" +checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" dependencies = [ "memchr", "thiserror", @@ -1350,9 +1398,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" +checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" dependencies = [ "pest", "pest_generator", @@ -1360,22 +1408,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" +checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] name = "pest_meta" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" +checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" dependencies = [ "once_cell", "pest", @@ -1481,7 +1529,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -1544,22 +1592,30 @@ dependencies = [ ] [[package]] -name = "ppv-lite86" -version = "0.2.17" +name = "polling" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "545c980a3880efd47b2e262f6a4bb6daad6555cf3367aa9c4e52895f69537a41" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix 0.38.30", + "tracing", + "windows-sys 0.52.0", +] [[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -1583,9 +1639,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c78e758510582acc40acb90458401172d41f1016f8c9dde89e49677afb7eec1" +checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" dependencies = [ "bytes", "rand", @@ -1607,16 +1663,16 @@ checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" dependencies = [ "bytes", "libc", - "socket2 0.5.4", + "socket2 0.5.5", "tracing", "windows-sys 0.48.0", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1653,29 +1709,29 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "redox_syscall", + "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", @@ -1685,9 +1741,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", @@ -1696,9 +1752,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "ring" @@ -1741,16 +1797,14 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.2" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" dependencies = [ - "byteorder", "const-oid", "digest", "num-bigint-dig", "num-integer", - "num-iter", "num-traits", "pkcs1", "pkcs8", @@ -1784,9 +1838,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.23" +version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ "bitflags 1.3.2", "errno", @@ -1798,15 +1852,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.14" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.2", "errno", "libc", - "linux-raw-sys 0.4.7", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.13", + "windows-sys 0.52.0", ] [[package]] @@ -1829,7 +1883,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -1844,13 +1898,13 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring 0.16.20", - "rustls-webpki 0.101.6", + "ring 0.17.7", + "rustls-webpki 0.101.7", "sct", ] @@ -1861,7 +1915,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.3", + "rustls-pemfile 1.0.4", "schannel", "security-framework", ] @@ -1881,9 +1935,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64", ] @@ -1906,12 +1960,12 @@ checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" [[package]] name = "rustls-webpki" -version = "0.101.6" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring 0.17.7", + "untrusted 0.9.0", ] [[package]] @@ -1927,24 +1981,24 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "schemars" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" dependencies = [ "dyn-clone", "schemars_derive", @@ -1954,9 +2008,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" dependencies = [ "proc-macro2", "quote", @@ -1972,12 +2026,12 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring 0.17.7", + "untrusted 0.9.0", ] [[package]] @@ -2015,28 +2069,28 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -2052,9 +2106,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", @@ -2063,9 +2117,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.25" +version = "0.9.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" +checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" dependencies = [ "indexmap", "itoa", @@ -2126,9 +2180,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", "rand_core", @@ -2145,15 +2199,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "3b187f0231d56fe41bfb12034819dd2bf336422a5866de41bc3fec4b2e3883e8" [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -2161,9 +2215,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", @@ -2186,9 +2240,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -2206,7 +2260,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af91f480ee899ab2d9f8435bfdfc14d08a5754bd9d3fef1f1a1c23336aad6c8b" dependencies = [ - "async-channel", + "async-channel 1.9.0", "cfg-if", "futures-core", "pin-project-lite", @@ -2237,9 +2291,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -2248,31 +2302,31 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -2301,9 +2355,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.32.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -2311,20 +2365,20 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.4", + "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -2341,11 +2395,10 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -2354,20 +2407,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] @@ -2429,9 +2482,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -2465,9 +2518,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "untrusted" @@ -2494,9 +2547,9 @@ dependencies = [ [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -2517,9 +2570,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" dependencies = [ "getrandom", ] @@ -2550,9 +2603,9 @@ dependencies = [ [[package]] name = "value-bag" -version = "1.4.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" +checksum = "7cdbaf5e132e593e9fc1de6a15bbec912395b11fb9719e061cf64f804524c503" [[package]] name = "vec_map" @@ -2586,9 +2639,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2596,24 +2649,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" dependencies = [ "cfg-if", "js-sys", @@ -2623,9 +2676,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2633,28 +2686,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen", @@ -2860,7 +2913,7 @@ dependencies = [ "rustc_version", "serde", "serde_json", - "socket2 0.5.4", + "socket2 0.5.5", "stop-token", "uhlc", "uuid", @@ -2960,7 +3013,7 @@ version = "0.10.1-rc" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4934fbb00820a45eb360af6c01538607a5e01d4fc0da201338b376ce2329c91a" dependencies = [ - "hashbrown 0.14.0", + "hashbrown 0.14.3", "keyed-set", "rand", "schemars", @@ -3086,7 +3139,7 @@ dependencies = [ "async-std", "async-trait", "log", - "socket2 0.5.4", + "socket2 0.5.5", "zenoh-buffers", "zenoh-collections", "zenoh-core", @@ -3146,7 +3199,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.37", + "syn 2.0.48", "unzip-n", "zenoh-keyexpr", ] @@ -3267,8 +3320,38 @@ dependencies = [ "zenoh-result", ] +[[package]] +name = "zenohex_nif" +version = "0.2.0-rc.2" +dependencies = [ + "flume", + "futures", + "rustler", + "zenoh", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/native/zenohex_nif/Cargo.toml b/native/zenohex_nif/Cargo.toml new file mode 100644 index 0000000..d444b93 --- /dev/null +++ b/native/zenohex_nif/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "zenohex_nif" +version = "0.2.0-rc.2" +authors = [] +edition = "2021" + +[lib] +name = "zenohex_nif" +path = "src/lib.rs" +crate-type = ["cdylib"] + +[dependencies] +flume = "0.11.0" +futures = "0.3.30" +rustler = { version = "0.30.0", default-features = false, features = ["derive", "nif_version_2_15"] } +zenoh = "0.10.1-rc" diff --git a/native/nifzenoh/README.md b/native/zenohex_nif/README.md similarity index 76% rename from native/nifzenoh/README.md rename to native/zenohex_nif/README.md index a5a73dc..2b386e9 100644 --- a/native/nifzenoh/README.md +++ b/native/zenohex_nif/README.md @@ -1,4 +1,4 @@ -# NIF for Elixir.NifZenoh +# NIF for Elixir.Zenohex.Nif ## To build the NIF module: @@ -7,8 +7,8 @@ ## To load the NIF: ```elixir -defmodule NifZenoh do - use Rustler, otp_app: :zenohex, crate: "nifzenoh" +defmodule Zenohex.Nif do + use Rustler, otp_app: :zenohex, crate: "zenohex_nif" # When your NIF is loaded, it will override this function. def add(_a, _b), do: :erlang.nif_error(:nif_not_loaded) diff --git a/native/zenohex_nif/src/config.rs b/native/zenohex_nif/src/config.rs new file mode 100644 index 0000000..0c3868a --- /dev/null +++ b/native/zenohex_nif/src/config.rs @@ -0,0 +1,56 @@ +use rustler::ErlOption; + +#[derive(rustler::NifStruct)] +#[module = "Zenohex.Config"] +pub(crate) struct ExConfig { + connect: ExConfigConnect, + scouting: ExConfigScouting, +} + +impl From for zenoh::prelude::config::Config { + fn from(value: ExConfig) -> Self { + let mut config = zenoh::prelude::config::peer(); + config.connect = value.connect.into(); + config.scouting = value.scouting.into(); + config + } +} + +#[derive(rustler::NifStruct)] +#[module = "Zenohex.Config.Connect"] +pub(crate) struct ExConfigConnect { + endpoints: Vec, +} + +impl From for zenoh::prelude::config::ConnectConfig { + fn from(value: ExConfigConnect) -> Self { + let endpoints = value + .endpoints + .iter() + .map(|endpoint| { + zenoh::prelude::config::EndPoint::try_from(endpoint.clone()) + .unwrap_or_else(|error| panic!("{}", error.to_string())) + }) + .collect(); + let mut config = zenoh::prelude::config::ConnectConfig::default(); + let _ = config.set_endpoints(endpoints); + config + } +} + +#[derive(rustler::NifStruct)] +#[module = "Zenohex.Config.Scouting"] +pub(crate) struct ExConfigScouting { + delay: ErlOption, +} + +impl From for zenoh::prelude::config::ScoutingConf { + fn from(value: ExConfigScouting) -> Self { + let mut config = zenoh::prelude::config::ScoutingConf::default(); + let _ = match Option::::from(value.delay) { + Some(delay) => config.set_delay(Some(delay)), + None => config.set_delay(None), + }; + config + } +} diff --git a/native/zenohex_nif/src/keyexpr.rs b/native/zenohex_nif/src/keyexpr.rs new file mode 100644 index 0000000..68aca83 --- /dev/null +++ b/native/zenohex_nif/src/keyexpr.rs @@ -0,0 +1,8 @@ +use zenoh::key_expr::{keyexpr, KeyExpr}; + +#[rustler::nif] +fn key_expr_intersects(key_expr1: &str, key_expr2: &str) -> bool { + let key_expr1 = unsafe { KeyExpr::from_str_uncheckend(key_expr1) }; + let key_expr2 = unsafe { KeyExpr::from_str_uncheckend(key_expr2) }; + keyexpr::intersects(&key_expr1, &key_expr2) +} diff --git a/native/zenohex_nif/src/lib.rs b/native/zenohex_nif/src/lib.rs new file mode 100644 index 0000000..8dea25a --- /dev/null +++ b/native/zenohex_nif/src/lib.rs @@ -0,0 +1,86 @@ +use std::sync::{Arc, RwLock}; + +use flume::Receiver; +use rustler::{Env, ResourceArc, Term}; +use zenoh::{ + prelude::sync::*, publication::Publisher, query::Reply, queryable::Query, queryable::Queryable, + sample::Sample, subscriber::PullSubscriber, subscriber::Subscriber, Session, +}; + +mod atoms { + rustler::atoms! { + timeout, + disconnected, + } +} +mod config; +mod keyexpr; +mod publisher; +mod pull_subscriber; +mod query; +mod queryable; +mod sample; +mod session; +mod subscriber; +mod value; + +struct SessionRef(Arc); +struct PublisherRef(Publisher<'static>); +struct SubscriberRef(Subscriber<'static, Receiver>); +struct PullSubscriberRef(PullSubscriber<'static, Receiver>); +struct QueryableRef(Queryable<'static, Receiver>); +struct ReplyReceiverRef(Receiver); +struct QueryRef(RwLock>); +struct SampleRef(Sample); + +#[rustler::nif(schedule = "DirtyIo")] +fn zenoh_open(config: crate::config::ExConfig) -> Result, String> { + let config: zenoh::prelude::config::Config = config.into(); + match zenoh::open(config).res_sync() { + Ok(session) => Ok(ResourceArc::new(SessionRef(session.into_arc()))), + Err(error) => Err(error.to_string()), + } +} + +fn load(env: Env, _term: Term) -> bool { + rustler::resource!(SessionRef, env); + rustler::resource!(PublisherRef, env); + rustler::resource!(SubscriberRef, env); + rustler::resource!(PullSubscriberRef, env); + rustler::resource!(QueryableRef, env); + rustler::resource!(ReplyReceiverRef, env); + rustler::resource!(QueryRef, env); + rustler::resource!(SampleRef, env); + true +} + +rustler::init!( + "Elixir.Zenohex.Nif", + [ + zenoh_open, + session::declare_publisher, + session::declare_subscriber, + session::declare_pull_subscriber, + session::declare_queryable, + session::session_put_integer, + session::session_put_float, + session::session_put_binary, + session::session_get_reply_receiver, + session::session_get_reply_timeout, + session::session_delete, + publisher::publisher_put_integer, + publisher::publisher_put_float, + publisher::publisher_put_binary, + publisher::publisher_delete, + publisher::publisher_congestion_control, + publisher::publisher_priority, + subscriber::subscriber_recv_timeout, + pull_subscriber::pull_subscriber_pull, + pull_subscriber::pull_subscriber_recv_timeout, + queryable::queryable_recv_timeout, + query::query_reply, + query::query_finish_reply, + keyexpr::key_expr_intersects, + ], + load = load +); diff --git a/native/zenohex_nif/src/publisher.rs b/native/zenohex_nif/src/publisher.rs new file mode 100644 index 0000000..e4e0754 --- /dev/null +++ b/native/zenohex_nif/src/publisher.rs @@ -0,0 +1,112 @@ +use crate::PublisherRef; +use rustler::{types::atom, Binary, Encoder, Env, ResourceArc, Term}; +use zenoh::{prelude::sync::SyncResolve, publication::Publisher, value::Value}; + +#[rustler::nif] +fn publisher_put_integer(env: Env, resource: ResourceArc, value: i64) -> Term { + publisher_put_impl(env, resource, value) +} + +#[rustler::nif] +fn publisher_put_float(env: Env, resource: ResourceArc, value: f64) -> Term { + publisher_put_impl(env, resource, value) +} + +#[rustler::nif] +fn publisher_put_binary<'a>( + env: Env<'a>, + resource: ResourceArc, + value: Binary<'a>, +) -> Term<'a> { + publisher_put_impl(env, resource, Value::from(value.as_slice())) +} + +fn publisher_put_impl>( + env: Env, + resource: ResourceArc, + value: T, +) -> Term { + let publisher: &Publisher = &resource.0; + match publisher.put(value).res_sync() { + Ok(_) => atom::ok().encode(env), + Err(error) => (atom::error(), error.to_string()).encode(env), + } +} + +#[rustler::nif] +fn publisher_delete(env: Env, resource: ResourceArc) -> Term { + let publisher: &Publisher = &resource.0; + match publisher.delete().res_sync() { + Ok(_) => atom::ok().encode(env), + Err(error) => (atom::error(), error.to_string()).encode(env), + } +} + +#[rustler::nif] +fn publisher_congestion_control( + resource: ResourceArc, + value: ExCongestionControl, +) -> ResourceArc { + let publisher: &Publisher = &resource.0; + let publisher: Publisher = publisher.clone().congestion_control(value.into()); + + ResourceArc::new(PublisherRef(publisher)) +} + +#[rustler::nif] +fn publisher_priority( + resource: ResourceArc, + value: ExPriority, +) -> ResourceArc { + let publisher: &Publisher = &resource.0; + let publisher: Publisher = publisher.clone().priority(value.into()); + + ResourceArc::new(PublisherRef(publisher)) +} + +#[derive(rustler::NifStruct)] +#[module = "Zenohex.Publisher.Options"] +pub(crate) struct ExPublisherOptions { + pub(crate) congestion_control: ExCongestionControl, + pub(crate) priority: ExPriority, +} + +#[derive(rustler::NifUnitEnum)] +pub(crate) enum ExCongestionControl { + Drop, + Block, +} + +impl From for zenoh::publication::CongestionControl { + fn from(value: ExCongestionControl) -> Self { + match value { + ExCongestionControl::Drop => zenoh::publication::CongestionControl::Drop, + ExCongestionControl::Block => zenoh::publication::CongestionControl::Block, + } + } +} + +#[derive(rustler::NifUnitEnum)] +pub(crate) enum ExPriority { + RealTime, + InteractiveHigh, + InteractiveLow, + DataHigh, + Data, + DataLow, + Background, +} + +impl From for zenoh::publication::Priority { + fn from(value: ExPriority) -> Self { + match value { + ExPriority::RealTime => zenoh::publication::Priority::RealTime, + ExPriority::InteractiveHigh => zenoh::publication::Priority::InteractiveHigh, + ExPriority::InteractiveLow => zenoh::publication::Priority::InteractiveLow, + ExPriority::DataHigh => zenoh::publication::Priority::DataHigh, + ExPriority::Data => zenoh::publication::Priority::Data, + ExPriority::DataLow => zenoh::publication::Priority::DataLow, + ExPriority::Background => zenoh::publication::Priority::Background, + } + } +} diff --git a/native/zenohex_nif/src/pull_subscriber.rs b/native/zenohex_nif/src/pull_subscriber.rs new file mode 100644 index 0000000..f832286 --- /dev/null +++ b/native/zenohex_nif/src/pull_subscriber.rs @@ -0,0 +1,28 @@ +use std::time::Duration; + +use flume::{Receiver, RecvTimeoutError}; +use rustler::{types::atom, Encoder, Env, ResourceArc, Term}; +use zenoh::{prelude::sync::SyncResolve, sample::Sample, subscriber::PullSubscriber}; + +#[rustler::nif(schedule = "DirtyIo")] +fn pull_subscriber_recv_timeout( + env: Env, + resource: ResourceArc, + timeout_us: u64, +) -> Result { + let pull_subscriber: &PullSubscriber<'_, Receiver> = &resource.0; + match pull_subscriber.recv_timeout(Duration::from_micros(timeout_us)) { + Ok(sample) => Ok(crate::sample::ExSample::from(env, sample).encode(env)), + Err(RecvTimeoutError::Timeout) => Err(crate::atoms::timeout().encode(env)), + Err(RecvTimeoutError::Disconnected) => Err(crate::atoms::disconnected().encode(env)), + } +} + +#[rustler::nif] +fn pull_subscriber_pull(env: Env, resource: ResourceArc) -> Term { + let pull_subscriber: &PullSubscriber<'_, Receiver> = &resource.0; + match pull_subscriber.pull().res_sync() { + Ok(_) => atom::ok().encode(env), + Err(error) => (atom::error(), error.to_string()).encode(env), + } +} diff --git a/native/zenohex_nif/src/query.rs b/native/zenohex_nif/src/query.rs new file mode 100644 index 0000000..88acecb --- /dev/null +++ b/native/zenohex_nif/src/query.rs @@ -0,0 +1,128 @@ +use std::sync::RwLock; + +use rustler::{types::atom, Encoder, Env, ErlOption, ResourceArc, Term}; +use zenoh::prelude::sync::SyncResolve; + +use crate::{QueryRef, SampleRef}; + +#[derive(rustler::NifStruct)] +#[module = "Zenohex.Query"] +pub(crate) struct ExQuery<'a> { + key_expr: String, + parameters: String, + value: ErlOption>, + reference: ResourceArc, +} + +impl ExQuery<'_> { + pub(crate) fn from(env: Env, query: zenoh::queryable::Query) -> ExQuery { + ExQuery { + key_expr: query.key_expr().to_string(), + parameters: query.parameters().to_string(), + value: match query.value() { + Some(value) => ErlOption::some(crate::value::ExValue::from(env, value)), + None => ErlOption::none(), + }, + reference: ResourceArc::new(QueryRef(RwLock::new(Some(query)))), + } + } +} + +#[rustler::nif(schedule = "DirtyIo")] +fn query_reply<'a>( + env: Env<'a>, + query: ExQuery<'a>, + sample: crate::sample::ExSample<'a>, +) -> Term<'a> { + let lock: &RwLock> = &query.reference.0; + let guard = match lock.read() { + Ok(guard) => guard, + Err(error) => return (atom::error(), error.to_string()).encode(env), + }; + let query: &zenoh::queryable::Query = match &*guard { + Some(query) => query, + None => { + return ( + atom::error(), + "ResponseFinal has already been sent".to_string(), + ) + .encode(env) + } + }; + let sample: zenoh::sample::Sample = + match Option::>::from(sample.reference.clone()) { + Some(resource) => resource.0.clone(), + None => sample.into(), + }; + match query.reply(Ok(sample)).res_sync() { + Ok(_) => atom::ok().encode(env), + Err(error) => (atom::error(), error.to_string()).encode(env), + } +} + +#[rustler::nif(schedule = "DirtyIo")] +fn query_finish_reply<'a>(env: Env<'a>, query: ExQuery<'a>) -> Term<'a> { + let lock: &RwLock> = &query.reference.0; + let mut guard = match lock.write() { + Ok(guard) => guard, + Err(error) => return (atom::error(), error.to_string()).encode(env), + }; + match Option::take(&mut *guard) { + Some(query) => { + // When Query drops, ResponseFinal is sent. + // So we need to drop the query at the end of the reply by calling this function. + drop(query); + atom::ok().encode(env) + } + None => { + return ( + atom::error(), + "ResponseFinal has already been sent".to_string(), + ) + .encode(env) + } + } +} + +#[derive(rustler::NifStruct)] +#[module = "Zenohex.Query.Options"] +pub(crate) struct ExQueryOptions { + pub(crate) target: ExQueryTarget, + pub(crate) consolidation: ExConsolidationMode, +} + +#[derive(rustler::NifUnitEnum)] +pub(crate) enum ExQueryTarget { + BestMatching, + All, + AllComplete, +} + +impl From for zenoh::query::QueryTarget { + fn from(value: ExQueryTarget) -> Self { + match value { + ExQueryTarget::BestMatching => zenoh::query::QueryTarget::BestMatching, + ExQueryTarget::All => zenoh::query::QueryTarget::All, + ExQueryTarget::AllComplete => zenoh::query::QueryTarget::AllComplete, + } + } +} + +#[derive(rustler::NifUnitEnum)] +pub(crate) enum ExConsolidationMode { + Auto, + None, + Monotonic, + Latest, +} + +impl From for zenoh::query::QueryConsolidation { + fn from(value: ExConsolidationMode) -> Self { + match value { + ExConsolidationMode::Auto => zenoh::query::Mode::Auto.into(), + ExConsolidationMode::None => zenoh::query::ConsolidationMode::None.into(), + ExConsolidationMode::Monotonic => zenoh::query::ConsolidationMode::Monotonic.into(), + ExConsolidationMode::Latest => zenoh::query::ConsolidationMode::Latest.into(), + } + } +} diff --git a/native/zenohex_nif/src/queryable.rs b/native/zenohex_nif/src/queryable.rs new file mode 100644 index 0000000..d1a7f13 --- /dev/null +++ b/native/zenohex_nif/src/queryable.rs @@ -0,0 +1,27 @@ +use std::time::Duration; + +use flume::{Receiver, RecvTimeoutError}; +use rustler::{Encoder, Env, ResourceArc, Term}; +use zenoh::queryable::{Query, Queryable}; + +use crate::{atoms, QueryableRef}; + +#[rustler::nif(schedule = "DirtyIo")] +fn queryable_recv_timeout( + env: Env, + resource: ResourceArc, + timeout_us: u64, +) -> Result { + let queryable: &Queryable<'_, Receiver> = &resource.0; + match queryable.recv_timeout(Duration::from_micros(timeout_us)) { + Ok(query) => Ok(crate::query::ExQuery::from(env, query).encode(env)), + Err(RecvTimeoutError::Timeout) => Err(atoms::timeout().encode(env)), + Err(RecvTimeoutError::Disconnected) => Err(atoms::disconnected().encode(env)), + } +} + +#[derive(rustler::NifStruct)] +#[module = "Zenohex.Queryable.Options"] +pub(crate) struct ExQueryableOptions { + pub(crate) complete: bool, +} diff --git a/native/zenohex_nif/src/sample.rs b/native/zenohex_nif/src/sample.rs new file mode 100644 index 0000000..3673f13 --- /dev/null +++ b/native/zenohex_nif/src/sample.rs @@ -0,0 +1,66 @@ +use rustler::{Binary, Env, ErlOption, ResourceArc, Term}; + +use crate::SampleRef; + +#[derive(rustler::NifStruct)] +#[module = "Zenohex.Sample"] +pub(crate) struct ExSample<'a> { + pub(crate) key_expr: String, + pub(crate) value: Term<'a>, + pub(crate) kind: ExSampleKind, + pub(crate) reference: ErlOption>, +} + +impl ExSample<'_> { + pub(crate) fn from(env: Env, sample: zenoh::sample::Sample) -> ExSample { + ExSample { + key_expr: sample.key_expr.to_string(), + value: crate::value::ExValue::from(env, &sample.value), + kind: sample.kind.into(), + reference: ErlOption::some(ResourceArc::new(SampleRef(sample))), + } + } +} + +impl From> for zenoh::sample::Sample { + fn from(sample: ExSample) -> Self { + let key_expr = unsafe { zenoh::key_expr::KeyExpr::from_string_unchecked(sample.key_expr) }; + let value = match sample.value.get_type() { + rustler::TermType::Atom => unimplemented!(), + rustler::TermType::Binary => { + let binary = sample.value.decode::().unwrap(); + zenoh::value::Value::from(binary.as_slice()) + } + rustler::TermType::Fun => unimplemented!(), + rustler::TermType::List => unimplemented!(), + rustler::TermType::Map => unimplemented!(), + rustler::TermType::Integer => { + zenoh::value::Value::from(sample.value.decode::().unwrap()) + } + rustler::TermType::Float => { + zenoh::value::Value::from(sample.value.decode::().unwrap()) + } + rustler::TermType::Pid => unimplemented!(), + rustler::TermType::Port => unimplemented!(), + rustler::TermType::Ref => unimplemented!(), + rustler::TermType::Tuple => unimplemented!(), + rustler::TermType::Unknown => unimplemented!(), + }; + zenoh::sample::Sample::new(key_expr, value) + } +} + +#[derive(rustler::NifUnitEnum)] +pub(crate) enum ExSampleKind { + Put, + Delete, +} + +impl From for ExSampleKind { + fn from(kind: zenoh::prelude::SampleKind) -> Self { + match kind { + zenoh::prelude::SampleKind::Put => ExSampleKind::Put, + zenoh::prelude::SampleKind::Delete => ExSampleKind::Delete, + } + } +} diff --git a/native/zenohex_nif/src/session.rs b/native/zenohex_nif/src/session.rs new file mode 100644 index 0000000..20a0a96 --- /dev/null +++ b/native/zenohex_nif/src/session.rs @@ -0,0 +1,165 @@ +use std::{sync::Arc, time::Duration}; + +use flume::{Receiver, RecvTimeoutError}; +use rustler::{types::atom, Binary, Encoder, Env, ResourceArc, Term}; +use zenoh::{prelude::sync::*, query::Reply, value::Value, Session}; + +#[rustler::nif] +fn declare_publisher( + resource: ResourceArc, + key_expr: String, + opts: crate::publisher::ExPublisherOptions, +) -> Result, String> { + let session: &Arc = &resource.0; + match session + .declare_publisher(key_expr) + .congestion_control(opts.congestion_control.into()) + .priority(opts.priority.into()) + .res_sync() + { + Ok(publisher) => Ok(ResourceArc::new(crate::PublisherRef(publisher))), + Err(error) => Err(error.to_string()), + } +} + +#[rustler::nif] +fn declare_subscriber( + resource: ResourceArc, + key_expr: String, + opts: crate::subscriber::SubscriberOptions, +) -> Result, String> { + let session: &Arc = &resource.0; + match session + .declare_subscriber(key_expr) + .reliability(opts.reliability.into()) + .res_sync() + { + Ok(subscriber) => Ok(ResourceArc::new(crate::SubscriberRef(subscriber))), + Err(error) => Err(error.to_string()), + } +} + +#[rustler::nif] +fn declare_pull_subscriber( + resource: ResourceArc, + key_expr: String, + opts: crate::subscriber::SubscriberOptions, +) -> Result, String> { + let session: &Arc = &resource.0; + match session + .declare_subscriber(key_expr) + .reliability(opts.reliability.into()) + .pull_mode() + .res_sync() + { + Ok(pull_subscriber) => Ok(ResourceArc::new(crate::PullSubscriberRef(pull_subscriber))), + Err(error) => Err(error.to_string()), + } +} + +#[rustler::nif] +fn declare_queryable( + resource: ResourceArc, + key_expr: String, + opts: crate::queryable::ExQueryableOptions, +) -> Result, String> { + let session: &Arc = &resource.0; + match session + .declare_queryable(key_expr) + .complete(opts.complete) + .res_sync() + { + Ok(queryable) => Ok(ResourceArc::new(crate::QueryableRef(queryable))), + Err(error) => Err(error.to_string()), + } +} + +#[rustler::nif] +fn session_put_integer( + env: Env, + resource: ResourceArc, + key_expr: String, + value: i64, +) -> Term { + session_put_impl(env, resource, key_expr, value) +} + +#[rustler::nif] +fn session_put_float( + env: Env, + resource: ResourceArc, + key_expr: String, + value: f64, +) -> Term { + session_put_impl(env, resource, key_expr, value) +} + +#[rustler::nif] +fn session_put_binary<'a>( + env: Env<'a>, + resource: ResourceArc, + key_expr: String, + value: Binary<'a>, +) -> Term<'a> { + session_put_impl(env, resource, key_expr, Value::from(value.as_slice())) +} + +fn session_put_impl>( + env: Env, + resource: ResourceArc, + key_expr: String, + value: T, +) -> Term { + let session: &Arc = &resource.0; + match session.put(key_expr, value).res_sync() { + Ok(_) => atom::ok().encode(env), + Err(error) => (atom::error(), error.to_string()).encode(env), + } +} + +#[rustler::nif] +fn session_get_reply_receiver( + resource: ResourceArc, + selector: String, + opts: crate::query::ExQueryOptions, +) -> Result, String> { + // NOTE: 引数に ExQuery を使うことが妥当と思うが、 + // zenoh の v1.0.0 前にそれらを考えるのが時期焦燥と判断し一旦このままとする + // TODO: with_value 対応も v1.0.0 が出たら検討する + let session: &Arc = &resource.0; + match session + .get(selector) + .target(opts.target.into()) + .consolidation(opts.consolidation) + .res_sync() + { + Ok(receiver) => Ok(ResourceArc::new(crate::ReplyReceiverRef(receiver))), + Err(error) => Err(error.to_string()), + } +} + +#[rustler::nif(schedule = "DirtyIo")] +fn session_get_reply_timeout( + env: Env, + resource: ResourceArc, + timeout_us: u64, +) -> Result { + let receiver: &Receiver = &resource.0; + match receiver.recv_timeout(Duration::from_micros(timeout_us)) { + Ok(reply) => match reply.sample { + Ok(sample) => Ok(crate::sample::ExSample::from(env, sample).encode(env)), + Err(value) => Err(crate::value::ExValue::from(env, &value).encode(env)), + }, + Err(RecvTimeoutError::Timeout) => Err(crate::atoms::timeout().encode(env)), + Err(RecvTimeoutError::Disconnected) => Err(crate::atoms::disconnected().encode(env)), + } +} + +#[rustler::nif] +fn session_delete(env: Env, resource: ResourceArc, key_expr: String) -> Term { + let session: &Arc = &resource.0; + match session.delete(key_expr).res_sync() { + Ok(_) => atom::ok().encode(env), + Err(error) => (atom::error(), error.to_string()).encode(env), + } +} diff --git a/native/zenohex_nif/src/subscriber.rs b/native/zenohex_nif/src/subscriber.rs new file mode 100644 index 0000000..d83ee69 --- /dev/null +++ b/native/zenohex_nif/src/subscriber.rs @@ -0,0 +1,40 @@ +use std::time::Duration; + +use flume::{Receiver, RecvTimeoutError}; +use rustler::{Encoder, Env, ResourceArc, Term}; +use zenoh::{sample::Sample, subscriber::Subscriber}; + +#[rustler::nif(schedule = "DirtyIo")] +fn subscriber_recv_timeout( + env: Env, + resource: ResourceArc, + timeout_us: u64, +) -> Result { + let subscriber: &Subscriber<'_, Receiver> = &resource.0; + match subscriber.recv_timeout(Duration::from_micros(timeout_us)) { + Ok(sample) => Ok(crate::sample::ExSample::from(env, sample).encode(env)), + Err(RecvTimeoutError::Timeout) => Err(crate::atoms::timeout().encode(env)), + Err(RecvTimeoutError::Disconnected) => Err(crate::atoms::disconnected().encode(env)), + } +} + +#[derive(rustler::NifStruct)] +#[module = "Zenohex.Subscriber.Options"] +pub(crate) struct SubscriberOptions { + pub(crate) reliability: ExReliability, +} + +#[derive(rustler::NifUnitEnum)] +pub(crate) enum ExReliability { + BestEffort, + Reliable, +} + +impl From for zenoh::subscriber::Reliability { + fn from(value: ExReliability) -> Self { + match value { + ExReliability::BestEffort => zenoh::subscriber::Reliability::BestEffort, + ExReliability::Reliable => zenoh::subscriber::Reliability::Reliable, + } + } +} diff --git a/native/zenohex_nif/src/value.rs b/native/zenohex_nif/src/value.rs new file mode 100644 index 0000000..bee2749 --- /dev/null +++ b/native/zenohex_nif/src/value.rs @@ -0,0 +1,50 @@ +use std::{borrow::Cow, io::Write}; + +use rustler::{Encoder, Env, OwnedBinary, Term}; +use zenoh::prelude::KnownEncoding; + +pub(crate) struct ExValue; + +impl ExValue { + pub(crate) fn from<'a>(env: Env<'a>, value: &zenoh::value::Value) -> Term<'a> { + match value.encoding.prefix() { + KnownEncoding::Empty => unimplemented!(), + KnownEncoding::AppOctetStream => match Cow::try_from(value) { + Ok(value) => { + let mut binary = OwnedBinary::new(value.len()).unwrap(); + binary.as_mut_slice().write_all(&value).unwrap(); + binary.release(env).encode(env) + } + Err(error) => panic!("{}", error.to_string()), + }, + KnownEncoding::AppCustom => unimplemented!(), + KnownEncoding::TextPlain => match String::try_from(value) { + Ok(value) => value.encode(env), + Err(error) => panic!("{}", error.to_string()), + }, + KnownEncoding::AppProperties => unimplemented!(), + KnownEncoding::AppJson => unimplemented!(), + KnownEncoding::AppSql => unimplemented!(), + KnownEncoding::AppInteger => match i64::try_from(value) { + Ok(value) => value.encode(env), + Err(error) => panic!("{}", error.to_string()), + }, + KnownEncoding::AppFloat => match f64::try_from(value) { + Ok(value) => value.encode(env), + Err(error) => panic!("{}", error.to_string()), + }, + KnownEncoding::AppXml => unimplemented!(), + KnownEncoding::AppXhtmlXml => unimplemented!(), + KnownEncoding::AppXWwwFormUrlencoded => unimplemented!(), + KnownEncoding::TextJson => unimplemented!(), + KnownEncoding::TextHtml => unimplemented!(), + KnownEncoding::TextXml => unimplemented!(), + KnownEncoding::TextCss => unimplemented!(), + KnownEncoding::TextCsv => unimplemented!(), + KnownEncoding::TextJavascript => unimplemented!(), + KnownEncoding::ImageJpeg => unimplemented!(), + KnownEncoding::ImagePng => unimplemented!(), + KnownEncoding::ImageGif => unimplemented!(), + } + } +} diff --git a/test/support/utils.ex b/test/support/utils.ex new file mode 100644 index 0000000..02535f8 --- /dev/null +++ b/test/support/utils.ex @@ -0,0 +1,11 @@ +defmodule Zenohex.Test.Utils do + @moduledoc false + + def maybe_different_session(session) do + if System.get_env("USE_DIFFERENT_SESSION") == "1" do + Zenohex.open!() + else + session + end + end +end diff --git a/test/version_match_test.exs b/test/version_match_test.exs new file mode 100644 index 0000000..0204ac2 --- /dev/null +++ b/test/version_match_test.exs @@ -0,0 +1,60 @@ +defmodule Zenohex.VersionMatchTest do + use ExUnit.Case + + describe "CI" do + test "version match" do + tool_versions_map = + File.read!(".tool-versions") + |> String.split("\n") + |> Enum.reduce(%{}, fn line, acc -> + cond do + String.contains?(line, "erlang") -> + [_, version] = String.split(line, " ") + Map.put(acc, :erlang, version) + + String.contains?(line, "elixir") -> + [_, version] = String.split(line, " ") + [version, "otp", _] = String.split(version, "-") + Map.put(acc, :elixir, version) + + true -> + acc + end + end) + + ciyaml_versions_map = + File.read!(".github/workflows/ci.yml") + |> String.split("\n") + |> Enum.reduce(%{}, fn line, acc -> + cond do + String.contains?(line, "OTP_VERSION: ") -> + [_, version] = String.split(line, ": ") + Map.put(acc, :erlang, version) + + String.contains?(line, "ELIXIR_VERSION: ") -> + [_, version] = String.split(line, ": ") + Map.put(acc, :elixir, version) + + true -> + acc + end + end) + + assert tool_versions_map.erlang == ciyaml_versions_map.erlang + assert tool_versions_map.elixir == ciyaml_versions_map.elixir + end + end + + describe "Elixir/Rust" do + test "version match" do + version_on_mix_exs = + Mix.Project.config() + |> Keyword.fetch!(:version) + + version_on_cargo_toml = + Toml.decode_file!("native/zenohex_nif/Cargo.toml")["package"]["version"] + + assert version_on_mix_exs == version_on_cargo_toml + end + end +end diff --git a/test/zenohex/config_test.exs b/test/zenohex/config_test.exs new file mode 100644 index 0000000..b3a3864 --- /dev/null +++ b/test/zenohex/config_test.exs @@ -0,0 +1,16 @@ +defmodule Zenohex.ConfigTest do + use ExUnit.Case + + alias Zenohex.Config + alias Zenohex.Config.Connect + alias Zenohex.Config.Scouting + + test "Zenohex.open(config)" do + config = %Config{ + connect: %Connect{endpoints: ["tcp/localhost:7447"]}, + scouting: %Scouting{delay: 200} + } + + assert {:ok, _session} = Zenohex.open(config) + end +end diff --git a/test/zenohex/examples/publisher_test.exs b/test/zenohex/examples/publisher_test.exs new file mode 100644 index 0000000..aaa1c72 --- /dev/null +++ b/test/zenohex/examples/publisher_test.exs @@ -0,0 +1,73 @@ +defmodule Zenohex.Examples.PublisherTest do + use ExUnit.Case + + import Zenohex.Test.Utils, only: [maybe_different_session: 1] + + alias Zenohex.Examples.Publisher + alias Zenohex.Examples.Subscriber + + setup do + {:ok, session} = Zenohex.open() + key_expr = "key/expression/pub" + + start_supervised!({Publisher, %{session: session, key_expr: key_expr}}) + + %{session: maybe_different_session(session)} + end + + describe "put/1" do + test "with subscriber", %{session: session} do + me = self() + + start_supervised!( + {Subscriber, + %{ + session: session, + key_expr: "key/expression/**", + callback: fn sample -> send(me, sample) end + }} + ) + + for i <- 0..100 do + assert Publisher.put(i) == :ok + assert_receive %Zenohex.Sample{key_expr: "key/expression/pub", kind: :put, value: ^i} + end + end + end + + describe "delete/0" do + test "with subscriber", %{session: session} do + me = self() + + start_supervised!( + {Subscriber, + %{ + session: session, + key_expr: "key/expression/**", + callback: fn sample -> send(me, sample) end + }} + ) + + assert Publisher.delete() == :ok + + if System.get_env("USE_DIFFERENT_SESSION") == "1" do + # Zenoh 0.10.1-rc has the bug, https://github.com/eclipse-zenoh/zenoh/issues/633 + # This bug causes that `delete` creates the Sample whose kind is :put. + # FIXME: when update Zenoh from 0.10.1-rc to over + assert_receive %Zenohex.Sample{key_expr: "key/expression/pub", kind: :put} + else + assert_receive %Zenohex.Sample{key_expr: "key/expression/pub", kind: :delete} + end + end + end + + test "congestion_control/1" do + assert Publisher.congestion_control(:block) == :ok + assert Publisher.put("put") == :ok + end + + test "priority/1" do + assert Publisher.priority(:real_time) == :ok + assert Publisher.put("put") == :ok + end +end diff --git a/test/zenohex/examples/pull_subscriber_test.exs b/test/zenohex/examples/pull_subscriber_test.exs new file mode 100644 index 0000000..0d2a33f --- /dev/null +++ b/test/zenohex/examples/pull_subscriber_test.exs @@ -0,0 +1,38 @@ +defmodule Zenohex.Examples.PullSubscriberTest do + use ExUnit.Case + + import Zenohex.Test.Utils, only: [maybe_different_session: 1] + + alias Zenohex.Examples.PullSubscriber + + setup do + {:ok, session} = Zenohex.open() + key_expr = "key/expression/**" + me = self() + callback = fn sample -> send(me, sample) end + + start_supervised!( + {PullSubscriber, %{session: session, key_expr: key_expr, callback: callback}} + ) + + %{session: maybe_different_session(session)} + end + + # WHY: skip this test when using different session + # When using same session, Zenoh pull subscriber can get Sample before pulling. + # But using different session, Zenoh pull subscriber can not. + # This might be a Zenoh bug. + @tag System.get_env("USE_DIFFERENT_SESSION") == "1" && :skip + test "start_link/1", %{session: session} do + Zenohex.Session.put(session, "key/expression/put", "put") + + assert_receive(%Zenohex.Sample{key_expr: "key/expression/put", value: "put"}) + end + + test "pull/0", %{session: session} do + Zenohex.Session.put(session, "key/expression/put", "put") + + PullSubscriber.pull() + assert_receive(%Zenohex.Sample{key_expr: "key/expression/put", value: "put"}) + end +end diff --git a/test/zenohex/examples/queryable_test.exs b/test/zenohex/examples/queryable_test.exs new file mode 100644 index 0000000..3125d2f --- /dev/null +++ b/test/zenohex/examples/queryable_test.exs @@ -0,0 +1,24 @@ +defmodule Zenohex.Examples.QueryableTest do + use ExUnit.Case + + import Zenohex.Test.Utils, only: [maybe_different_session: 1] + + alias Zenohex.Examples.Queryable + + setup do + {:ok, session} = Zenohex.open() + key_expr = "key/expression/**" + me = self() + callback = fn query -> send(me, query) end + + start_supervised!({Queryable, %{session: session, key_expr: key_expr, callback: callback}}) + + %{session: maybe_different_session(session)} + end + + test "start_link/1", %{session: session} do + Zenohex.Session.get_timeout(session, "key/expression/**", 1000) + + assert_receive(%Zenohex.Query{}) + end +end diff --git a/test/zenohex/examples/session_test.exs b/test/zenohex/examples/session_test.exs new file mode 100644 index 0000000..6b6d185 --- /dev/null +++ b/test/zenohex/examples/session_test.exs @@ -0,0 +1,84 @@ +defmodule Zenohex.Examples.SessionTest do + use ExUnit.Case + + import Zenohex.Test.Utils, only: [maybe_different_session: 1] + + alias Zenohex.Examples.Session + alias Zenohex.Examples.Subscriber + alias Zenohex.Examples.Queryable + + setup do + {:ok, session} = Zenohex.open() + + start_supervised!({Session, %{session: session}}) + + %{session: maybe_different_session(session)} + end + + describe "put/2" do + test "with subscriber", %{session: session} do + me = self() + + start_supervised!( + {Subscriber, + %{ + session: session, + key_expr: "key/expression/**", + callback: fn sample -> send(me, sample) end + }} + ) + + assert Session.put("key/expression/put", "value") == :ok + assert_receive %Zenohex.Sample{key_expr: "key/expression/put", value: "value"} + end + end + + describe "delete/1" do + test "with subscriber", %{session: session} do + me = self() + + start_supervised!( + {Subscriber, + %{ + session: session, + key_expr: "key/expression/**", + callback: fn sample -> send(me, sample) end + }} + ) + + assert Session.delete("key/expression/delete") == :ok + assert_receive %Zenohex.Sample{key_expr: "key/expression/delete", kind: :delete} + end + end + + describe "get/2" do + test "without queryable" do + me = self() + callback = fn sample -> send(me, sample) end + :ok = Session.set_disconnected_cb(fn -> send(me, :disconnected) end) + + assert Session.get("key/expression/**", callback) == :ok + assert_receive :disconnected + end + + test "with queryable", %{session: session} do + start_supervised!( + {Queryable, + %{ + session: session, + key_expr: "key/expression/**", + callback: fn query -> + :ok = Zenohex.Query.reply(query, %Zenohex.Sample{key_expr: "key/expression/reply"}) + :ok = Zenohex.Query.finish_reply(query) + end + }} + ) + + me = self() + callback = fn sample -> send(me, sample) end + + assert Session.get("key/expression/**", callback) == :ok + assert_receive %Zenohex.Sample{} + end + end +end diff --git a/test/zenohex/examples/storage_test.exs b/test/zenohex/examples/storage_test.exs new file mode 100644 index 0000000..e9edb68 --- /dev/null +++ b/test/zenohex/examples/storage_test.exs @@ -0,0 +1,75 @@ +defmodule Zenohex.Examples.StorageTest do + use ExUnit.Case + + import Zenohex.Test.Utils, only: [maybe_different_session: 1] + + alias Zenohex.Examples.Storage + + setup do + {:ok, session} = Zenohex.open() + + start_supervised!({Storage, %{session: session, key_expr: "demo/example/**"}}) + + %{session: maybe_different_session(session)} + end + + test "put", %{session: session} do + key_expr = "demo/example/put" + value = 0 + :ok = Zenohex.Session.put(session, key_expr, value) + + confirm_put(key_expr, value) + end + + test "delete", %{session: session} do + key_expr = "demo/example/delete" + value = 0 + :ok = Zenohex.Session.put(session, key_expr, value) + confirm_put(key_expr, value) + + :ok = Zenohex.Session.delete(session, key_expr) + confirm_delete(key_expr) + end + + test "get", %{session: session} do + key_expr = "demo/example/get" + value = 0 + :ok = Zenohex.Session.put(session, key_expr, value) + confirm_put(key_expr, value) + + confirm_get(session, key_expr, value) + end + + defp confirm_put(key_expr, value, retry_count \\ 10) when retry_count > 0 do + case Storage.Store.get(key_expr) do + {:ok, [sample]} -> + assert sample.value == value + + {:error, :not_found} -> + Process.sleep(1) + confirm_put(key_expr, value, retry_count - 1) + end + end + + defp confirm_delete(key_expr, retry_count \\ 10) when retry_count > 0 do + case Storage.Store.get(key_expr) do + {:error, :not_found} -> + assert true + + {:ok, [_sample]} -> + Process.sleep(1) + confirm_delete(key_expr, retry_count - 1) + end + end + + defp confirm_get(session, key_expr, value, retry_count \\ 10) when retry_count > 0 do + case Zenohex.Session.get_timeout(session, key_expr, 1000) do + {:ok, sample} -> + assert sample.value == value + + {:error, :timeout} -> + Process.sleep(1) + confirm_get(session, key_expr, value, retry_count - 1) + end + end +end diff --git a/test/zenohex/examples/subscriber_test.exs b/test/zenohex/examples/subscriber_test.exs new file mode 100644 index 0000000..b755069 --- /dev/null +++ b/test/zenohex/examples/subscriber_test.exs @@ -0,0 +1,24 @@ +defmodule Zenohex.Examples.SubscriberTest do + use ExUnit.Case + + import Zenohex.Test.Utils, only: [maybe_different_session: 1] + + alias Zenohex.Examples.Subscriber + + setup do + {:ok, session} = Zenohex.open() + key_expr = "key/expression/**" + me = self() + callback = fn sample -> send(me, sample) end + + start_supervised!({Subscriber, %{session: session, key_expr: key_expr, callback: callback}}) + + %{session: maybe_different_session(session)} + end + + test "start_link/1", %{session: session} do + Zenohex.Session.put(session, "key/expression/put", "put") + + assert_receive(%Zenohex.Sample{key_expr: "key/expression/put", value: "put"}) + end +end diff --git a/test/zenohex/nif_test.exs b/test/zenohex/nif_test.exs new file mode 100644 index 0000000..d777428 --- /dev/null +++ b/test/zenohex/nif_test.exs @@ -0,0 +1,182 @@ +defmodule Zenohex.NifTest do + use ExUnit.Case + + alias Zenohex.Nif + alias Zenohex.Sample + + setup_all do + {:ok, session} = Nif.zenoh_open() + %{session: session} + end + + describe "session" do + for {type, value} <- [ + {"integer", 0}, + {"float", 0.0}, + {"binary", :erlang.term_to_binary("binary")} + ] do + test "session_put_#{type}/2", %{session: session} do + type = unquote(type) + value = unquote(value) + assert apply(Nif, :"session_put_#{type}", [session, "key/expression", value]) == :ok + end + end + + test "session_get_reply_receiver/3", %{session: session} do + {:ok, receiver} = Nif.session_get_reply_receiver(session, "key_expression") + assert is_reference(receiver) + end + + test "session_get_reply_timeout/2", %{session: session} do + {:ok, receiver} = Nif.session_get_reply_receiver(session, "key_expression") + assert Nif.session_get_reply_timeout(receiver, 1000) == {:error, :disconnected} + end + + test "session_delete/2", %{session: session} do + assert Nif.session_delete(session, "key_expression") == :ok + end + end + + describe "publisher" do + alias Zenohex.Publisher.Options + + test "declare_publisher/2", %{session: session} do + {:ok, publisher} = Nif.declare_publisher(session, "key/expression") + assert is_reference(publisher) + end + + test "declare_publisher/3", %{session: session} do + opts = %Options{congestion_control: :block, priority: :real_time} + {:ok, publisher} = Nif.declare_publisher(session, "key/expression", opts) + assert is_reference(publisher) + end + + test "publisher_congestion_control/2", %{session: session} do + {:ok, publisher} = Nif.declare_publisher(session, "key/expression") + assert is_reference(Nif.publisher_congestion_control(publisher, :block)) + end + + test "publisher_priority/2", %{session: session} do + {:ok, publisher} = Nif.declare_publisher(session, "key/expression") + assert is_reference(Nif.publisher_priority(publisher, :real_time)) + end + + for {type, value} <- [ + {"integer", 0}, + {"float", 0.0}, + {"binary", :erlang.term_to_binary("binary")} + ] do + test "publisher_put_#{type}/2", %{session: session} do + type = unquote(type) + value = unquote(value) + {:ok, publisher} = Nif.declare_publisher(session, "key/expression") + assert apply(Nif, :"publisher_put_#{type}", [publisher, value]) == :ok + end + end + + test "publisher_delete/1", %{session: session} do + {:ok, publisher} = Nif.declare_publisher(session, "key/expression") + assert Nif.publisher_delete(publisher) == :ok + end + end + + describe "subscriber" do + alias Zenohex.Subscriber.Options + + test "declare_subscriber/2", %{session: session} do + {:ok, subscriber} = Nif.declare_subscriber(session, "key/expression") + assert is_reference(subscriber) + end + + test "declare_subscriber/3", %{session: session} do + opts = %Options{reliability: :reliable} + {:ok, subscriber} = Nif.declare_subscriber(session, "key/expression", opts) + assert is_reference(subscriber) + end + + test "subscriber_recv_timeout/1", %{session: session} do + {:ok, publisher} = Nif.declare_publisher(session, "key/expression") + {:ok, subscriber} = Nif.declare_subscriber(session, "key/expression") + + Nif.publisher_put_integer(publisher, 0) + assert {:ok, %Sample{value: 0}} = Nif.subscriber_recv_timeout(subscriber, 1000) + + Nif.publisher_put_float(publisher, 0.0) + assert {:ok, %Sample{value: 0.0}} = Nif.subscriber_recv_timeout(subscriber, 1000) + + Nif.publisher_put_binary(publisher, "binary") + assert {:ok, %Sample{value: "binary"}} = Nif.subscriber_recv_timeout(subscriber, 1000) + assert Nif.subscriber_recv_timeout(subscriber, 1000) == {:error, :timeout} + end + end + + describe "pull subscriber" do + alias Zenohex.Subscriber.Options + + test "declare_pull_subscriber/2", %{session: session} do + {:ok, pull_subscriber} = Nif.declare_pull_subscriber(session, "key/expression") + assert is_reference(pull_subscriber) + end + + test "declare_pull_subscriber/3", %{session: session} do + opts = %Options{reliability: :reliable} + {:ok, pull_subscriber} = Nif.declare_pull_subscriber(session, "key/expression", opts) + assert is_reference(pull_subscriber) + end + + test "pull_subscriber_pull/1", %{session: session} do + {:ok, publisher} = Nif.declare_publisher(session, "key/expression") + {:ok, pull_subscriber} = Nif.declare_pull_subscriber(session, "key/expression") + + :ok = Nif.publisher_put_integer(publisher, 0) + {:ok, %Sample{value: 0}} = Nif.pull_subscriber_recv_timeout(pull_subscriber, 1000) + {:error, :timeout} = Nif.pull_subscriber_recv_timeout(pull_subscriber, 1000) + assert Nif.pull_subscriber_pull(pull_subscriber) == :ok + assert {:ok, %Sample{value: 0}} = Nif.pull_subscriber_recv_timeout(pull_subscriber, 1000) + end + end + + describe "binary pub/sub" do + setup context do + key_expr = "key/expression" + {:ok, publisher} = Nif.declare_publisher(context.session, key_expr) + {:ok, subscriber} = Nif.declare_subscriber(context.session, key_expr) + %{publisher: publisher, subscriber: subscriber} + end + + for {test_name, binary} <- [ + {"empty binary", ""}, + {"erlang term binary", :erlang.term_to_binary(%URI{})} + ] do + test "#{test_name}", %{publisher: publisher, subscriber: subscriber} do + binary = unquote(binary) + Nif.publisher_put_binary(publisher, binary) + assert {:ok, %Sample{value: ^binary}} = Nif.subscriber_recv_timeout(subscriber, 1000) + end + end + end + + describe "queryable" do + alias Zenohex.Queryable.Options + + test "declare_queryable/2", %{session: session} do + {:ok, queryable} = Nif.declare_queryable(session, "key/expression") + assert is_reference(queryable) + end + + test "declare_queryable/3", %{session: session} do + opts = %Options{complete: true} + {:ok, queryable} = Nif.declare_queryable(session, "key/expression", opts) + assert is_reference(queryable) + end + end + + describe "key_expr" do + test "key_expr_intersects/2" do + assert Nif.key_expr_intersects("key/expression/**", "key/expression/demo") + assert Nif.key_expr_intersects("key/expression/demo", "key/expression/**") + refute Nif.key_expr_intersects("key/expression/**", "key/value") + refute Nif.key_expr_intersects("key/value", "key/expression/**") + end + end +end diff --git a/test/zenohex/publisher_test.exs b/test/zenohex/publisher_test.exs new file mode 100644 index 0000000..460cfd5 --- /dev/null +++ b/test/zenohex/publisher_test.exs @@ -0,0 +1,4 @@ +defmodule Zenohex.PublisherTest do + use ExUnit.Case + doctest Zenohex.Publisher +end diff --git a/test/zenohex/pull_subscriber_test.exs b/test/zenohex/pull_subscriber_test.exs new file mode 100644 index 0000000..8eb9c1d --- /dev/null +++ b/test/zenohex/pull_subscriber_test.exs @@ -0,0 +1,4 @@ +defmodule Zenohex.PullSubscriberTest do + use ExUnit.Case + doctest Zenohex.PullSubscriber +end diff --git a/test/zenohex/queryable_test.exs b/test/zenohex/queryable_test.exs new file mode 100644 index 0000000..dad7ca3 --- /dev/null +++ b/test/zenohex/queryable_test.exs @@ -0,0 +1,4 @@ +defmodule Zenohex.QueryableTest do + use ExUnit.Case + doctest Zenohex.Queryable +end diff --git a/test/zenohex/session_test.exs b/test/zenohex/session_test.exs new file mode 100644 index 0000000..33fecb6 --- /dev/null +++ b/test/zenohex/session_test.exs @@ -0,0 +1,4 @@ +defmodule Zenohex.SessionTest do + use ExUnit.Case + doctest Zenohex.Session +end diff --git a/test/zenohex/subscriber_test.exs b/test/zenohex/subscriber_test.exs new file mode 100644 index 0000000..0a73301 --- /dev/null +++ b/test/zenohex/subscriber_test.exs @@ -0,0 +1,4 @@ +defmodule Zenohex.SubscriberTest do + use ExUnit.Case + doctest Zenohex.Subscriber +end diff --git a/test/zenohex_test.exs b/test/zenohex_test.exs index bbd6788..9f1e7d1 100644 --- a/test/zenohex_test.exs +++ b/test/zenohex_test.exs @@ -2,7 +2,20 @@ defmodule ZenohexTest do use ExUnit.Case doctest Zenohex - test "greets the world" do - assert Zenohex.hello() == :world + alias Zenohex.Sample + + test "pub/sub" do + {:ok, session} = Zenohex.open() + {:ok, publisher} = Zenohex.Session.declare_publisher(session, "pub/sub") + {:ok, subscriber} = Zenohex.Session.declare_subscriber(session, "pub/sub") + + for i <- 1..100 do + :ok = Zenohex.Publisher.put(publisher, "Hello Zenoh Dragon #{i}") + + {:ok, %Sample{key_expr: "pub/sub", value: value, kind: :put}} = + Zenohex.Subscriber.recv_timeout(subscriber) + + assert value == "Hello Zenoh Dragon #{i}" + end end end