From 887f0e91ca2c502a653f66a21d16379fcbd50dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 13:42:10 +0200 Subject: [PATCH 01/48] move to astral's fork of dist --- .github/workflows/release.yml | 18 ++++++++++++------ dist-workspace.toml | 4 +++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d17ef807..9455a258 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,7 @@ -# This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/ +# This file was autogenerated by dist: https://github.com/astral-sh/cargo-dist # # Copyright 2022-2024, axodotdev +# Copyright 2025 Astral Software Inc. # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: @@ -47,7 +48,7 @@ on: jobs: # Run 'dist plan' (or host) to determine what tasks we need to do plan: - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ !github.event.pull_request && github.ref_name || '' }} @@ -58,12 +59,13 @@ jobs: steps: - uses: actions/checkout@v4 with: + persist-credentials: false submodules: recursive - name: Install dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash - run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.28.0/cargo-dist-installer.sh | sh" + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.5/cargo-dist-installer.sh | sh" - name: Cache dist uses: actions/upload-artifact@v4 with: @@ -117,6 +119,7 @@ jobs: git config --global core.longpaths true - uses: actions/checkout@v4 with: + persist-credentials: false submodules: recursive - name: Install Rust non-interactively if not already installed if: ${{ matrix.container }} @@ -172,13 +175,14 @@ jobs: needs: - plan - build-local-artifacts - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v4 with: + persist-credentials: false submodules: recursive - name: Install cached dist uses: actions/download-artifact@v4 @@ -222,12 +226,13 @@ jobs: if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@v4 with: + persist-credentials: false submodules: recursive - name: Install cached dist uses: actions/download-artifact@v4 @@ -286,10 +291,11 @@ jobs: # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! if: ${{ always() && needs.host.result == 'success' }} - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: + persist-credentials: false submodules: recursive diff --git a/dist-workspace.toml b/dist-workspace.toml index 5e546cfb..44c52272 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -3,8 +3,10 @@ members = ["cargo:."] # Config for 'dist' [dist] +# Whether +crt-static should be used on msvc +msvc-crt-static = false # The preferred dist version to use in CI (Cargo.toml SemVer syntax) -cargo-dist-version = "0.28.0" +cargo-dist-version = "0.28.5" # CI backends to support ci = "github" # The installers to generate for each app From 1cf03cc8c82b73a4d501acbf1e14d6f771da652a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 13:52:40 +0200 Subject: [PATCH 02/48] overwrite cargo-dist to get patch --- .github/workflows/release.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9455a258..ed9519a4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,12 @@ jobs: # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash - run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.5/cargo-dist-installer.sh | sh" + # Need to overwrite it to fix: https://github.com/astral-sh/cargo-dist/pull/36 + run: cargo install cargo-dist \ + --git https://github.com/CramBL/cargo-dist \ + --branch fix-ignored-msvc-crt-static-flag \ + --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6 + #run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.5/cargo-dist-installer.sh | sh" - name: Cache dist uses: actions/upload-artifact@v4 with: From 9819dd2e9e478155fecaefd383ba2ce075a20ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 13:59:09 +0200 Subject: [PATCH 03/48] set just env vars --- .github/workflows/CI.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f6cda4bd..2ae5763f 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -17,6 +17,8 @@ env: RUSTDOCFLAGS: -D warnings RUST_BACKTRACE: 1 RUST_LOG: debug + JUST_VERBOSE: 1 + JUST_COLOR: always jobs: check: From 4145873e500642969ea046e885346e0eba442880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 14:00:46 +0200 Subject: [PATCH 04/48] use custom build setup --- .github/workflows/build-setup.yml | 20 ++++++++++++++++++++ .github/workflows/release.yml | 21 +++++++++++++++------ dist-workspace.toml | 3 +++ 3 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/build-setup.yml diff --git a/.github/workflows/build-setup.yml b/.github/workflows/build-setup.yml new file mode 100644 index 00000000..a07f5b42 --- /dev/null +++ b/.github/workflows/build-setup.yml @@ -0,0 +1,20 @@ +- name: Install Just + uses: extractions/setup-just@v3 +- name: Install HDF5 + shell: bash + run: just init::install-hdf5-headers + +- name: Run MSI installer (windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + cd just\hdf + msiexec /i HDF5-1.14.0-win64.msi /quiet /qn /norestart + +# Need to overwrite it to fix: https://github.com/astral-sh/cargo-dist/pull/36 +- name: Use patched cargo-dist version (windows) + if: runner.os == 'Windows' + run: cargo install cargo-dist \ + --git https://github.com/CramBL/cargo-dist \ + --branch fix-ignored-msvc-crt-static-flag \ + --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6 \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ed9519a4..bbd6957b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,12 +65,7 @@ jobs: # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash - # Need to overwrite it to fix: https://github.com/astral-sh/cargo-dist/pull/36 - run: cargo install cargo-dist \ - --git https://github.com/CramBL/cargo-dist \ - --branch fix-ignored-msvc-crt-static-flag \ - --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6 - #run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.5/cargo-dist-installer.sh | sh" + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.5/cargo-dist-installer.sh | sh" - name: Cache dist uses: actions/upload-artifact@v4 with: @@ -133,6 +128,20 @@ jobs: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH fi + - name: "Install Just" + uses: "extractions/setup-just@v3" + - name: "Install HDF5" + run: "just init::install-hdf5-headers" + shell: "bash" + - name: "Run MSI installer (windows)" + if: "runner.os == 'Windows'" + run: | + cd just\hdf + msiexec /i HDF5-1.14.0-win64.msi /quiet /qn /norestart + shell: "pwsh" + - name: "Use patched cargo-dist version (windows)" + if: "runner.os == 'Windows'" + run: "cargo install cargo-dist \\ --git https://github.com/CramBL/cargo-dist \\ --branch fix-ignored-msvc-crt-static-flag \\ --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6" - uses: swatinem/rust-cache@v2 with: key: ${{ join(matrix.targets, '-') }} diff --git a/dist-workspace.toml b/dist-workspace.toml index 44c52272..c4ed4555 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -1,6 +1,8 @@ [workspace] members = ["cargo:."] +allow-dirty = true + # Config for 'dist' [dist] # Whether +crt-static should be used on msvc @@ -19,3 +21,4 @@ install-path = "CARGO_HOME" install-updater = false # Which actions to run on pull requests pr-run-mode = "upload" +github-build-setup = "build-setup.yml" From ccde44f58deccbdd65ecef481dd266d7ee730614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 14:06:15 +0200 Subject: [PATCH 05/48] fix --- Justfile | 8 ++------ just/init.just | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/Justfile b/Justfile index 613560d2..d2ee0588 100644 --- a/Justfile +++ b/Justfile @@ -1,6 +1,7 @@ import 'just/mod.just' # CI only recipes, `just -l ci` to list them mod ci 'just/ci.just' +mod init 'just/init.just' set windows-shell := ["powershell.exe", "-c"] @@ -24,12 +25,7 @@ export RUST_LOG := env_var_or_default("RUST_LOG", "info") export PLOTINATOR_BYPASS_UPDATES := env_var_or_default("PLOTINATOR_BYPASS_UPDATES", "true") @_default: - just --list --no-aliases - -[group("Init")] -init: install-devtools - @echo "Run {{BOLD + YELLOW}}install-extra-devtools{{NORMAL}} for some adittional productivity tools that fit into the existent workflow" - @echo "Run {{BOLD + YELLOW}}apt-install-hdf5-header{{NORMAL}} to get HDF5 headers for developing HDF5 features on linux" + just --list [doc("Checks both native and wasm"), group("Check"), no-exit-message] check-all: check check-wasm diff --git a/just/init.just b/just/init.just index 47c3f6e4..36adf9a1 100644 --- a/just/init.just +++ b/just/init.just @@ -1,3 +1,9 @@ +[group("Init")] +init: install-devtools + @echo "Run {{BOLD + YELLOW}}install-extra-devtools{{NORMAL}} for some adittional productivity tools that fit into the existent workflow" + @echo "Run {{BOLD + YELLOW}}apt-install-hdf5-header{{NORMAL}} to get HDF5 headers for developing HDF5 features on linux" + + # Trunk is used to serve the app with a webserver, cargo-dist is used to generate and update workflows for distributing installers for various platforms [group("Init"), doc("Install the required tools for performing all dev tasks for the project")] install-devtools: @@ -15,4 +21,36 @@ install-extra-devtools: [group("Init")] apt-install-hdf5-header: - sudo apt install libhdf5-dev \ No newline at end of file + sudo apt install libhdf5-dev + +[windows] +msi-install-hdf5-headers: + #!/usr/bin/env bash + set -eo pipefail + VERSION="1.14.0" + DL_URL="https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-1.14/hdf5-${VERSION}/bin/windows/hdf5-${VERSION}-Std-win10_64-vs16.zip" + # Download and extract HDF5 + curl -L -o hdf5.zip "${DL_URL}" + unzip hdf5.zip + if [[ "${GITHUB_ACTIONS}" == "true" ]]; then + just init::set-github-hdf5-env-vars "${VERSION}" + fi + + +[windows] +set-github-hdf5-env-vars VERSION: + echo "HDF5_DIR=C:\\Program Files\\HDF_Group\\HDF5\\{{VERSION}}" >> $GITHUB_ENV + echo "C:\\Program Files\\HDF_Group\\HDF5\\{{VERSION}}\\bin" >> $GITHUB_PATH + +[macos] +brew-install-hdf5-headers: + brew install hdf5 + +[linux] +install-hdf5-headers: apt-install-hdf5-headers + +[macos] +install-hdf5-headers: brew-install-hdf5-headers + +[windows] +install-hdf5-headers: msi-install-hdf5-headers \ No newline at end of file From fc5f08a6d875ac6b569b17e40e3e7204b8cddee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 14:10:04 +0200 Subject: [PATCH 06/48] tweak --- Justfile | 1 - just/init.just | 2 ++ just/mod.just | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Justfile b/Justfile index d2ee0588..4f09452e 100644 --- a/Justfile +++ b/Justfile @@ -7,7 +7,6 @@ set windows-shell := ["powershell.exe", "-c"] PROJECT_NAME := "plotinator3000" -alias i := init alias t := test alias l := lint alias fmt := format diff --git a/just/init.just b/just/init.just index 36adf9a1..08fded33 100644 --- a/just/init.just +++ b/just/init.just @@ -1,3 +1,5 @@ +set windows-shell := ["powershell.exe", "-c"] + [group("Init")] init: install-devtools @echo "Run {{BOLD + YELLOW}}install-extra-devtools{{NORMAL}} for some adittional productivity tools that fit into the existent workflow" diff --git a/just/mod.just b/just/mod.just index 73e6941e..af88a6ee 100644 --- a/just/mod.just +++ b/just/mod.just @@ -1,3 +1,2 @@ import 'default_cmd.just' -import 'init.just' import 'mqtt.just' From aa002759c873fe12f0cc7a68a69910c8dd3bc572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 14:14:24 +0200 Subject: [PATCH 07/48] fix typo --- Justfile | 1 + just/init.just | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Justfile b/Justfile index 4f09452e..c402d066 100644 --- a/Justfile +++ b/Justfile @@ -1,6 +1,7 @@ import 'just/mod.just' # CI only recipes, `just -l ci` to list them mod ci 'just/ci.just' +# init only recipes, `just -l init` to list them mod init 'just/init.just' set windows-shell := ["powershell.exe", "-c"] diff --git a/just/init.just b/just/init.just index 08fded33..c93d6872 100644 --- a/just/init.just +++ b/just/init.just @@ -3,7 +3,7 @@ set windows-shell := ["powershell.exe", "-c"] [group("Init")] init: install-devtools @echo "Run {{BOLD + YELLOW}}install-extra-devtools{{NORMAL}} for some adittional productivity tools that fit into the existent workflow" - @echo "Run {{BOLD + YELLOW}}apt-install-hdf5-header{{NORMAL}} to get HDF5 headers for developing HDF5 features on linux" + @echo "Run {{BOLD + YELLOW}}apt-install-hdf5-headers{{NORMAL}} to get HDF5 headers for developing HDF5 features on linux" # Trunk is used to serve the app with a webserver, cargo-dist is used to generate and update workflows for distributing installers for various platforms @@ -22,7 +22,7 @@ install-extra-devtools: cargo install bacon --locked [group("Init")] -apt-install-hdf5-header: +apt-install-hdf5-headers: sudo apt install libhdf5-dev [windows] From cd48d222d7df9475aef20f19920b2ed7171aecd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 14:25:27 +0200 Subject: [PATCH 08/48] ensure update functionality is not bypassed in CI --- .cargo/config.toml | 4 ++++ src/updater.rs | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index ac2b23f8..25d69f04 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,6 @@ [target.x86_64-pc-windows-msvc] rustflags = ["-C", "target-feature=+crt-static"] + +[env] +# We generally want to bypass updates in the build, they should only really be run in CI as they touch many parts of the host +PLOTINATOR_BYPASS_UPDATES = "true" diff --git a/src/updater.rs b/src/updater.rs index 9c45e520..68d30251 100644 --- a/src/updater.rs +++ b/src/updater.rs @@ -209,7 +209,15 @@ pub fn update_if_applicable() -> axoupdater::AxoupdateResult { /// Check for the environment variable to bypass updates fn bypass_updates() -> bool { + // This is generally set in the build environment (see config.toml) if let Ok(value) = env::var(BYPASS_UPDATES_ENV_VAR) { + // If we're in the build environment and we detect CI, we don't allow bypassing updates + if let Ok(value) = env::var("GITHUB_ACTIONS") { + if value == "true" { + log::info!("GitHub actions detected, disabling bypass updates"); + return false; + } + } if value == "1" || value.eq_ignore_ascii_case("true") { log::info!("Update bypassed due to environment variable."); return true; From 8a4321ea5af317a5a3df9d4f1a793771a9302a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 14:26:55 +0200 Subject: [PATCH 09/48] dont escape newline, breaks cause they are double escaped by dist --- .github/workflows/build-setup.yml | 5 +---- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-setup.yml b/.github/workflows/build-setup.yml index a07f5b42..4dd5b5e8 100644 --- a/.github/workflows/build-setup.yml +++ b/.github/workflows/build-setup.yml @@ -14,7 +14,4 @@ # Need to overwrite it to fix: https://github.com/astral-sh/cargo-dist/pull/36 - name: Use patched cargo-dist version (windows) if: runner.os == 'Windows' - run: cargo install cargo-dist \ - --git https://github.com/CramBL/cargo-dist \ - --branch fix-ignored-msvc-crt-static-flag \ - --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6 \ No newline at end of file + run: cargo install cargo-dist --git https://github.com/CramBL/cargo-dist --branch fix-ignored-msvc-crt-static-flag --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6 \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bbd6957b..06e89597 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -141,7 +141,7 @@ jobs: shell: "pwsh" - name: "Use patched cargo-dist version (windows)" if: "runner.os == 'Windows'" - run: "cargo install cargo-dist \\ --git https://github.com/CramBL/cargo-dist \\ --branch fix-ignored-msvc-crt-static-flag \\ --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6" + run: "cargo install cargo-dist --git https://github.com/CramBL/cargo-dist --branch fix-ignored-msvc-crt-static-flag --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6" - uses: swatinem/rust-cache@v2 with: key: ${{ join(matrix.targets, '-') }} From 8c9bdb4f52f837801868c927abc47206eed6c82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 14:33:35 +0200 Subject: [PATCH 10/48] change location of build-setup.yml --- .github/{workflows => custom_build_setup}/build-setup.yml | 0 dist-workspace.toml | 8 +++----- 2 files changed, 3 insertions(+), 5 deletions(-) rename .github/{workflows => custom_build_setup}/build-setup.yml (100%) diff --git a/.github/workflows/build-setup.yml b/.github/custom_build_setup/build-setup.yml similarity index 100% rename from .github/workflows/build-setup.yml rename to .github/custom_build_setup/build-setup.yml diff --git a/dist-workspace.toml b/dist-workspace.toml index c4ed4555..b443989b 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -1,12 +1,8 @@ [workspace] members = ["cargo:."] -allow-dirty = true - # Config for 'dist' [dist] -# Whether +crt-static should be used on msvc -msvc-crt-static = false # The preferred dist version to use in CI (Cargo.toml SemVer syntax) cargo-dist-version = "0.28.5" # CI backends to support @@ -21,4 +17,6 @@ install-path = "CARGO_HOME" install-updater = false # Which actions to run on pull requests pr-run-mode = "upload" -github-build-setup = "build-setup.yml" +# Whether +crt-static should be used on msvc +msvc-crt-static = false +github-build-setup = "../custom_build_setup/build-setup.yml" From c879fdc15b05c3a13640a20258aa155f7d7e8bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 14:45:48 +0200 Subject: [PATCH 11/48] dont set crt static in config.toml --- .cargo/config.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 25d69f04..be89a788 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,3 @@ -[target.x86_64-pc-windows-msvc] -rustflags = ["-C", "target-feature=+crt-static"] - [env] # We generally want to bypass updates in the build, they should only really be run in CI as they touch many parts of the host PLOTINATOR_BYPASS_UPDATES = "true" From ff4381180d0812c6841449d9ea5bb052e45aac32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 14:53:30 +0200 Subject: [PATCH 12/48] use dirty release.yml --- .github/custom_build_setup/build-setup.yml | 5 ----- .github/workflows/release.yml | 8 +++++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/custom_build_setup/build-setup.yml b/.github/custom_build_setup/build-setup.yml index 4dd5b5e8..4d78bf0a 100644 --- a/.github/custom_build_setup/build-setup.yml +++ b/.github/custom_build_setup/build-setup.yml @@ -10,8 +10,3 @@ run: | cd just\hdf msiexec /i HDF5-1.14.0-win64.msi /quiet /qn /norestart - -# Need to overwrite it to fix: https://github.com/astral-sh/cargo-dist/pull/36 -- name: Use patched cargo-dist version (windows) - if: runner.os == 'Windows' - run: cargo install cargo-dist --git https://github.com/CramBL/cargo-dist --branch fix-ignored-msvc-crt-static-flag --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6 \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 06e89597..13b80405 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -139,15 +139,17 @@ jobs: cd just\hdf msiexec /i HDF5-1.14.0-win64.msi /quiet /qn /norestart shell: "pwsh" - - name: "Use patched cargo-dist version (windows)" - if: "runner.os == 'Windows'" - run: "cargo install cargo-dist --git https://github.com/CramBL/cargo-dist --branch fix-ignored-msvc-crt-static-flag --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6" - uses: swatinem/rust-cache@v2 with: key: ${{ join(matrix.targets, '-') }} cache-provider: ${{ matrix.cache_provider }} - name: Install dist run: ${{ matrix.install_dist.run }} + + # Need to overwrite it to fix: https://github.com/astral-sh/cargo-dist/pull/36 + - name: Use patched cargo-dist version (windows) + if: runner.os == 'Windows' + run: cargo install cargo-dist --git https://github.com/CramBL/cargo-dist --branch fix-ignored-msvc-crt-static-flag --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6 # Get the dist-manifest - name: Fetch local artifacts uses: actions/download-artifact@v4 From 89cbf7fb9f7736456cebbdf16032f771c6bdbb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 14:55:29 +0200 Subject: [PATCH 13/48] set allow dirty --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 13b80405..969a7d76 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -78,7 +78,7 @@ jobs: # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | - dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json + dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json --allow-dirty > plan-dist-manifest.json echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" @@ -163,7 +163,7 @@ jobs: - name: Build artifacts run: | # Actually do builds and make zips and whatnot - dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json + dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --allow-dirty --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "dist ran successfully" - id: cargo-dist name: Post-build @@ -216,7 +216,7 @@ jobs: - id: cargo-dist shell: bash run: | - dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json + dist build ${{ needs.plan.outputs.tag-flag }} --allow-dirty --output-format=json "--artifacts=global" > dist-manifest.json echo "dist ran successfully" # Parse out what we just built and upload it to scratch storage @@ -266,7 +266,7 @@ jobs: - id: host shell: bash run: | - dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json + dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --allow-dirty --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" From 4c981e6ffa539472347810eac9c8d5199c585225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 15:06:54 +0200 Subject: [PATCH 14/48] force install --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 969a7d76..ff526c8c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -149,7 +149,7 @@ jobs: # Need to overwrite it to fix: https://github.com/astral-sh/cargo-dist/pull/36 - name: Use patched cargo-dist version (windows) if: runner.os == 'Windows' - run: cargo install cargo-dist --git https://github.com/CramBL/cargo-dist --branch fix-ignored-msvc-crt-static-flag --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6 + run: cargo install cargo-dist --git https://github.com/CramBL/cargo-dist --branch fix-ignored-msvc-crt-static-flag --rev a4cabb10395c7b275ea40d1636cd2d0e58be8af6 --force # Get the dist-manifest - name: Fetch local artifacts uses: actions/download-artifact@v4 From 241de708c9c644af424e50547562378471a91edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 17:06:51 +0200 Subject: [PATCH 15/48] handle some sys topics --- crates/plotinator_mqtt/src/parse_packet.rs | 2 +- .../src/parse_packet/known_topic.rs | 31 ++++++++++++++----- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/crates/plotinator_mqtt/src/parse_packet.rs b/crates/plotinator_mqtt/src/parse_packet.rs index e396f990..95e9dbf8 100644 --- a/crates/plotinator_mqtt/src/parse_packet.rs +++ b/crates/plotinator_mqtt/src/parse_packet.rs @@ -8,7 +8,7 @@ pub mod known_topic; pub(crate) fn parse_packet(topic: &str, payload: &str) -> Option { if let Ok(known) = KnownTopic::from_str(topic) { match known.parse_packet(payload) { - Ok(mp) => Some(mp), + Ok(mp) => mp, Err(e) => { log::error!("{e}"); debug_assert!(false, "{e}"); diff --git a/crates/plotinator_mqtt/src/parse_packet/known_topic.rs b/crates/plotinator_mqtt/src/parse_packet/known_topic.rs index c9af6c5d..c517998f 100644 --- a/crates/plotinator_mqtt/src/parse_packet/known_topic.rs +++ b/crates/plotinator_mqtt/src/parse_packet/known_topic.rs @@ -35,6 +35,11 @@ pub(crate) enum KnownTopic { PilotDisplayHeading, #[strum(serialize = "closest_line")] PilotDisplayClosestLine, + #[strum(serialize = "$SYS/broker/uptime")] + SYSBrokerUptime, + // We cannot meaningfully plot this, but we use it to show the version when choosing a broker to connect to + #[strum(serialize = "$SYS/broker/version")] + SYSBrokerVersion } /// Debug packet with a single value @@ -61,27 +66,27 @@ pub(crate) struct DebugSensorsGps { } impl KnownTopic { - pub(crate) fn parse_packet(self, p: &str) -> anyhow::Result { + pub(crate) fn parse_packet(self, p: &str) -> anyhow::Result> { match self { Self::PilotDisplaySpeed => { let p = serde_json::from_str::(p)?; let value = p.speed()?; - Ok(self.into_single_mqtt_data(value)) + Ok(Some(self.into_single_mqtt_data(value))) } Self::PilotDisplayAltitude => { let p: PilotDisplayAltitudePacket = serde_json::from_str(p)?; let value = p.height()?; - Ok(self.into_single_mqtt_data(value)) + Ok(Some(self.into_single_mqtt_data(value))) } Self::PilotDisplayHeading => { let p: PilotDisplayHeadingPacket = serde_json::from_str(p)?; let value = p.heading()?; - Ok(self.into_single_mqtt_data(value)) + Ok(Some(self.into_single_mqtt_data(value))) } Self::PilotDisplayClosestLine => { let p: PilotDisplayClosestLinePacket = serde_json::from_str(p)?; let distance = p.distance(); - Ok(self.into_single_mqtt_data(distance)) + Ok(Some(self.into_single_mqtt_data(distance))) } // Debug topics for development and for inspiration for how to implement parsing of various kinds of // topics and payloads @@ -89,14 +94,14 @@ impl KnownTopic { | Self::DebugSensorsHumidity | Self::DebugSensorsPressure => { let sp: DebugSensorPacket = serde_json::from_str(p)?; - Ok(self.into_single_mqtt_data(sp.value)) + Ok(Some(self.into_single_mqtt_data(sp.value))) } Self::DebugSensorsGps => { let sp: DebugSensorsGps = serde_json::from_str(p)?; let td1 = MqttTopicData::single(self.subtopic_str("lat"), sp.value1); let td2 = MqttTopicData::single(self.subtopic_str("lon"), sp.value2); let d = MqttData::multiple(vec![td1, td2]); - Ok(d) + Ok(Some(d)) } Self::DebugSensorsMag => { let values: Vec = @@ -109,7 +114,17 @@ impl KnownTopic { points.push(p); } let td = MqttTopicData::multiple(self.to_string(), points); - Ok(MqttData::single(td)) + Ok(Some(MqttData::single(td))) + } + Self::SYSBrokerUptime => { + // Example payload: '2256144 seconds' + let uptime_str = p.trim_end_matches(" seconds"); + let uptime: f64 = uptime_str.parse()?; + Ok(Some(self.into_single_mqtt_data(uptime))) + } + Self::SYSBrokerVersion => { + // Does not make sense to plot + Ok(None) } } } From f618d6d0a82d56d5a5c771717e6c98b0a3cdc821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 17:07:13 +0200 Subject: [PATCH 16/48] refactor --- crates/plotinator_mqtt/src/mqtt_listener.rs | 10 +++------- crates/plotinator_mqtt/src/topic_discoverer.rs | 9 +++++---- crates/plotinator_mqtt/src/util.rs | 7 +++++++ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/crates/plotinator_mqtt/src/mqtt_listener.rs b/crates/plotinator_mqtt/src/mqtt_listener.rs index bc36d4b0..03059e89 100644 --- a/crates/plotinator_mqtt/src/mqtt_listener.rs +++ b/crates/plotinator_mqtt/src/mqtt_listener.rs @@ -7,7 +7,7 @@ use std::{ time::Duration, }; -use crate::data::listener::MqttData; +use crate::{data::listener::MqttData, util::timestamped_client_id}; pub fn mqtt_listener( tx: &mpsc::Sender, @@ -47,18 +47,14 @@ pub fn mqtt_listener( } fn setup_client(broker_host: String, broker_port: u16) -> (rumqttc::Client, rumqttc::Connection) { - let timestamp_id = std::time::SystemTime::now() - .duration_since(std::time::SystemTime::UNIX_EPOCH) - .expect("Time went backwards") - .as_millis(); let mut mqttoptions = MqttOptions::new( - format!("plotinator3000-{timestamp_id}"), + timestamped_client_id("plotinator3000"), broker_host, broker_port, ); mqttoptions.set_keep_alive(Duration::from_secs(5)); - Client::new(mqttoptions, 100) + Client::new(mqttoptions, 10000) } fn handle_event_packet(tx: &mpsc::Sender, packet: rumqttc::Publish) { diff --git a/crates/plotinator_mqtt/src/topic_discoverer.rs b/crates/plotinator_mqtt/src/topic_discoverer.rs index 62a69d0c..7e3c0e74 100644 --- a/crates/plotinator_mqtt/src/topic_discoverer.rs +++ b/crates/plotinator_mqtt/src/topic_discoverer.rs @@ -8,6 +8,8 @@ use std::{ time::Duration, }; +use crate::util::timestamped_client_id; + #[derive(Default)] pub(crate) struct TopicDiscoverer { active: bool, @@ -88,10 +90,7 @@ pub(crate) fn start_discovery( std::thread::Builder::new() .name("mqtt-topic-discoverer".into()) .spawn(move || { - let timestamp = std::time::SystemTime::now() - .duration_since(std::time::SystemTime::UNIX_EPOCH) - .expect("Time went backwards"); - let client_id = format!("discover-{}", timestamp.as_millis()); + let client_id = timestamped_client_id("discover"); log::info!( "Subscribing for discovery with id={client_id}, broker address={host}:{port}" @@ -139,3 +138,5 @@ pub(crate) fn start_discovery( }) .expect("Failed to start MQTT topic discoverer thread") } + + diff --git a/crates/plotinator_mqtt/src/util.rs b/crates/plotinator_mqtt/src/util.rs index 1877f2a1..32eb8458 100644 --- a/crates/plotinator_mqtt/src/util.rs +++ b/crates/plotinator_mqtt/src/util.rs @@ -37,3 +37,10 @@ pub(crate) fn parse_timestamp_to_nanos_f64( // Total nanoseconds since epoch Ok((seconds * 1_000_000_000.0) + nanos) } + +pub(crate) fn timestamped_client_id(name: impl Into) -> String { + let mut client_id = name.into(); + client_id.push('-'); + client_id.push_str(&now_timestamp().to_string()); + client_id +} \ No newline at end of file From 55a86d5aff035915d0b27fdfbea7358bcb5d0115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 17:53:11 +0200 Subject: [PATCH 17/48] show broker version if it can be resolved --- .../plotinator_mqtt/src/broker_validator.rs | 111 +++++++++++++++--- crates/plotinator_mqtt/src/lib.rs | 3 +- crates/plotinator_mqtt/src/mqtt_cfg_window.rs | 9 +- src/mqtt_window.rs | 60 ++++++---- 4 files changed, 142 insertions(+), 41 deletions(-) diff --git a/crates/plotinator_mqtt/src/broker_validator.rs b/crates/plotinator_mqtt/src/broker_validator.rs index 244a8d84..120345e7 100644 --- a/crates/plotinator_mqtt/src/broker_validator.rs +++ b/crates/plotinator_mqtt/src/broker_validator.rs @@ -1,21 +1,43 @@ use std::{ - net::{Ipv6Addr, TcpStream, ToSocketAddrs as _}, + net::{Ipv6Addr, SocketAddr, TcpStream, ToSocketAddrs as _}, sync::mpsc, time::{Duration, Instant}, }; +use rumqttc::{Client, Event, MqttOptions, Packet}; +use crate::util::timestamped_client_id; + +#[derive(Debug, Default, Clone, PartialEq)] +pub enum BrokerStatus { + #[default] + None, + Reachable, + Unreachable(String), + ReachableVersion(String) +} + +impl BrokerStatus { + pub fn reachable(&self) -> bool { + match self { + BrokerStatus::Reachable | + BrokerStatus::ReachableVersion(_) => true, + BrokerStatus::Unreachable(_) | + BrokerStatus::None => false, + } + } +} #[derive(Default)] pub(crate) struct BrokerValidator { previous_broker_input: String, - broker_status: Option>, + broker_status: BrokerStatus, validation_in_progress: bool, last_input_change: Option, - broker_validation_receiver: Option>>, + broker_validation_receiver: Option>, } impl BrokerValidator { - pub fn broker_status(&self) -> Option<&Result<(), String>> { - self.broker_status.as_ref() + pub fn broker_status(&self) -> &BrokerStatus { + &self.broker_status } pub fn validation_in_progress(&self) -> bool { @@ -29,10 +51,10 @@ impl BrokerValidator { if current_broker_input != self.previous_broker_input { self.previous_broker_input = current_broker_input.clone(); self.last_input_change = Some(Instant::now()); - self.broker_status = None; + self.broker_status = BrokerStatus::None; } - // Debounce and validate after 500ms + // Debounce and validate after a timeout if let Some(last_change) = self.last_input_change { if last_change.elapsed() >= Duration::from_millis(500) && !self.validation_in_progress { let (tx, rx) = std::sync::mpsc::channel(); @@ -45,9 +67,31 @@ impl BrokerValidator { if let Err(e) = std::thread::Builder::new() .name("broker-validator".into()) .spawn(move || { - let result = validate_broker(&cp_host, &cp_port); - if let Err(e) = tx.send(result) { - log::error!("{e}"); + match validate_broker(&cp_host, &cp_port) { + Ok(addr) => { + // First send that it's reachable + if let Err(e) = tx.send(BrokerStatus::Reachable) { + log::error!("{e}"); + return; + } + + // Then try to get the version + match get_broker_version(addr) { + Ok(version) => { + if let Err(e) = tx.send(BrokerStatus::ReachableVersion(version)) { + log::error!("{e}"); + } + } + Err(e) => { + log::warn!("Failed to get broker version: {e}"); + // Keep the Reachable status since we at least know it's reachable + } + } + + }, + Err(e) => if let Err(e) = tx.send(BrokerStatus::Unreachable(e)) { + log::error!("{e}"); + }, } }) { @@ -60,15 +104,18 @@ impl BrokerValidator { // Check for validation results, if we got a result we store the result and reset the check status if let Some(receiver) = &mut self.broker_validation_receiver { if let Ok(result) = receiver.try_recv() { - self.broker_status = Some(result); - self.validation_in_progress = false; - self.broker_validation_receiver = None; + self.broker_status = result; + // If the broker is reachable we continue so we can resolve its version + if self.broker_status != BrokerStatus::Reachable { + self.validation_in_progress = false; + self.broker_validation_receiver = None; + } } } } } -fn validate_broker(host: &str, port: &str) -> Result<(), String> { +fn validate_broker(host: &str, port: &str) -> Result { // Validate port first let port: u16 = port.parse().map_err(|e| format!("Invalid port: {e}"))?; @@ -91,7 +138,7 @@ fn validate_broker(host: &str, port: &str) -> Result<(), String> { let mut last_error = None; for addr in addrs { match TcpStream::connect_timeout(&addr, Duration::from_secs(2)) { - Ok(_) => return Ok(()), + Ok(_) => return Ok(addr), Err(e) => last_error = Some(e), } } @@ -101,3 +148,37 @@ fn validate_broker(host: &str, port: &str) -> Result<(), String> { |e| format!("Connection failed: {e}"), )) } + +fn get_broker_version(addr: SocketAddr) -> Result { + let client_id = timestamped_client_id("version-check"); + let mut mqttoptions = MqttOptions::new(client_id, addr.ip().to_string(), addr.port()); + mqttoptions.set_keep_alive(Duration::from_secs(5)); + let (client, mut connection) = Client::new(mqttoptions, 100); + + // Subscribe to the version topic + if let Err(e) = client.subscribe("$SYS/broker/version", rumqttc::QoS::AtMostOnce) { + return Err(format!("Failed to subscribe to version topic: {e}")); + } + + // Wait for the version message with a timeout + let start = Instant::now(); + let timeout = Duration::from_secs(2); + + while start.elapsed() < timeout { + match connection.iter().next() { + Some(Ok(Event::Incoming(Packet::Publish(publish)))) => { + if publish.topic == "$SYS/broker/version" { + if let Ok(version) = String::from_utf8(publish.payload.to_vec()) { + log::info!("Got broker version: {version}"); + return Ok(version); + } + } + } + Some(Err(e)) => return Err(format!("Connection error: {e}")), + None => break, + _ => continue, + } + } + + Err("Timeout waiting for broker version".to_string()) +} diff --git a/crates/plotinator_mqtt/src/lib.rs b/crates/plotinator_mqtt/src/lib.rs index f5c215e1..4ec7712b 100644 --- a/crates/plotinator_mqtt/src/lib.rs +++ b/crates/plotinator_mqtt/src/lib.rs @@ -11,5 +11,6 @@ non_wasm_modules!( ); #[cfg(not(target_arch = "wasm32"))] pub use crate::{ - data::plot::MqttPlotPoints, data_receiver::MqttDataReceiver, mqtt_cfg_window::MqttConfigWindow, + broker_validator::BrokerStatus, data::plot::MqttPlotPoints, data_receiver::MqttDataReceiver, + mqtt_cfg_window::MqttConfigWindow, }; diff --git a/crates/plotinator_mqtt/src/mqtt_cfg_window.rs b/crates/plotinator_mqtt/src/mqtt_cfg_window.rs index aea5920d..211054d7 100644 --- a/crates/plotinator_mqtt/src/mqtt_cfg_window.rs +++ b/crates/plotinator_mqtt/src/mqtt_cfg_window.rs @@ -7,7 +7,7 @@ use std::{ }; use crate::{ - broker_validator::BrokerValidator, data_receiver::MqttDataReceiver, + broker_validator::{BrokerStatus, BrokerValidator}, data_receiver::MqttDataReceiver, topic_discoverer::TopicDiscoverer, }; @@ -27,6 +27,11 @@ impl MqttConfigWindow { &self.selected_topics } + /// Returns whether or not the selected topics contains `topic` + pub fn selected_topics_contains(&self, topic: &str) -> bool { + self.selected_topics.iter().any(|t| t == topic) + } + pub fn selected_topics_as_mut(&mut self) -> &mut [String] { &mut self.selected_topics } @@ -106,7 +111,7 @@ impl MqttConfigWindow { self.topic_discoverer.discovered_topics_sorted() } - pub fn broker_status(&self) -> Option<&Result<(), String>> { + pub fn broker_status(&self) -> &BrokerStatus { self.broker_validator.broker_status() } diff --git a/src/mqtt_window.rs b/src/mqtt_window.rs index 2c4c10b8..c03a05c5 100644 --- a/src/mqtt_window.rs +++ b/src/mqtt_window.rs @@ -2,6 +2,7 @@ use egui::Color32; use egui::RichText; use egui::ScrollArea; use egui::Ui; +use plotinator_mqtt::BrokerStatus; use plotinator_mqtt::{MqttConfigWindow, MqttDataReceiver}; /// Shows the MQTT configuration window and returns a receiver channel if connect was clicked @@ -14,6 +15,7 @@ pub fn show_mqtt_window( let mut connect_clicked = false; egui::Window::new("MQTT Configuration") .open(mqtt_cfg_window_open) + .scroll([false, true]) .show(ctx, |ui| { ui.group(|ui| { ui.label("MQTT Broker Address"); @@ -28,7 +30,7 @@ pub fn show_mqtt_window( if mqtt_cfg_window.validation_in_progress() { ui.horizontal(|ui| { ui.spinner(); - ui.label("Checking broker..."); + ui.label("Checking for broker..."); }); } else { show_broker_status(ui, mqtt_cfg_window.broker_status()); @@ -44,7 +46,7 @@ pub fn show_mqtt_window( } }); - let discover_enabled = mqtt_cfg_window.broker_status().is_some_and(|s| s.is_ok()) + let discover_enabled = mqtt_cfg_window.broker_status().reachable() && !mqtt_cfg_window.discovery_active(); if !mqtt_cfg_window.discovery_active() @@ -74,13 +76,15 @@ pub fn show_mqtt_window( mqtt_cfg_window.discovered_topics().len() )); - ScrollArea::vertical().max_height(200.0).show(ui, |ui| { + ScrollArea::vertical().max_height(800.0).show(ui, |ui| { let topics: Vec<_> = mqtt_cfg_window.discovered_topics_sorted(); for topic in &topics { ui.horizontal(|ui| { - if ui.selectable_label(false, topic).clicked() { - mqtt_cfg_window.add_selected_topic(topic.to_string()); + if !mqtt_cfg_window.selected_topics_contains(topic) { + if ui.selectable_label(false, topic).clicked() { + mqtt_cfg_window.add_selected_topic(topic.to_string()); + } } }); } @@ -101,28 +105,38 @@ pub fn show_mqtt_window( data_receiver } -fn show_broker_status(ui: &mut Ui, broker_status: Option<&Result<(), String>>) { - if let Some(status) = broker_status { - match status { - Ok(()) => { - ui.colored_label( - egui::Color32::GREEN, - RichText::new(format!( - "{} Broker reachable", - egui_phosphor::regular::CHECK - )), - ); - } - Err(err) => { - ui.colored_label( - egui::Color32::RED, - RichText::new(format!("{} {err}", egui_phosphor::regular::WARNING_OCTAGON)), - ); - } +fn show_broker_status(ui: &mut Ui, broker_status: &BrokerStatus) { + match broker_status { + BrokerStatus::None => (), + BrokerStatus::Reachable => { + draw_reachable_label(ui, None); + } + BrokerStatus::Unreachable(err) => { + ui.colored_label( + egui::Color32::RED, + RichText::new(format!( + "{icon} {err}", + icon = egui_phosphor::regular::WARNING_OCTAGON + )), + ); + } + BrokerStatus::ReachableVersion(v) => { + draw_reachable_label(ui, Some(v.as_ref())); } } } +fn draw_reachable_label(ui: &mut Ui, version: Option<&str>) { + ui.colored_label( + egui::Color32::GREEN, + RichText::new(format!( + "{icon} {desc}", + icon = egui_phosphor::regular::CHECK, + desc = version.unwrap_or("Broker reachable") + )), + ); +} + fn show_active_discovery_status(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { if ui .button(format!( From 7834a2b7dd4d2a1f3a135844a486a5ae0acf597c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 18:51:00 +0200 Subject: [PATCH 18/48] improve MQTT connect window --- crates/plotinator_mqtt/src/mqtt_cfg_window.rs | 8 + .../plotinator_mqtt/src/topic_discoverer.rs | 28 ++- src/mqtt_window.rs | 189 ++++++++++-------- 3 files changed, 145 insertions(+), 80 deletions(-) diff --git a/crates/plotinator_mqtt/src/mqtt_cfg_window.rs b/crates/plotinator_mqtt/src/mqtt_cfg_window.rs index 211054d7..46a05684 100644 --- a/crates/plotinator_mqtt/src/mqtt_cfg_window.rs +++ b/crates/plotinator_mqtt/src/mqtt_cfg_window.rs @@ -111,6 +111,14 @@ impl MqttConfigWindow { self.topic_discoverer.discovered_topics_sorted() } + pub fn discovered_sys_topics(&self) -> &HashSet { + self.topic_discoverer.discovered_sys_topics() + } + + pub fn discovered_sys_topics_sorted(&self) -> Vec { + self.topic_discoverer.discovered_sys_topics_sorted() + } + pub fn broker_status(&self) -> &BrokerStatus { self.broker_validator.broker_status() } diff --git a/crates/plotinator_mqtt/src/topic_discoverer.rs b/crates/plotinator_mqtt/src/topic_discoverer.rs index 7e3c0e74..3c12610c 100644 --- a/crates/plotinator_mqtt/src/topic_discoverer.rs +++ b/crates/plotinator_mqtt/src/topic_discoverer.rs @@ -14,6 +14,7 @@ use crate::util::timestamped_client_id; pub(crate) struct TopicDiscoverer { active: bool, discovered_topics: HashSet, + discovered_sys_topics: HashSet, discovery_rx: Option>, stop_discovery_flag: Arc, discovery_handle: Option>, @@ -43,6 +44,7 @@ impl TopicDiscoverer { while let Ok(msg) = rx.try_recv() { match msg { DiscoveryMsg::Topic(t) => self.discovered_topics.insert(t), + DiscoveryMsg::SysTopic(t) => self.discovered_sys_topics.insert(t), DiscoveryMsg::Err(e) => return Err(e), }; } @@ -74,10 +76,21 @@ impl TopicDiscoverer { topics.sort(); topics } + + pub fn discovered_sys_topics(&self) -> &HashSet { + &self.discovered_sys_topics + } + + pub fn discovered_sys_topics_sorted(&self) -> Vec { + let mut topics: Vec = self.discovered_sys_topics.iter().cloned().collect(); + topics.sort(); + topics + } } pub(crate) enum DiscoveryMsg { Topic(String), + SysTopic(String), Err(String), } @@ -107,6 +120,14 @@ pub(crate) fn start_discovery( return; } + if let Err(e) = client.subscribe("$SYS/#", rumqttc::QoS::AtMostOnce) { + log::error!("Subscribe err={e}"); + if let Err(e) = tx.send(DiscoveryMsg::Err(e.to_string())) { + log::error!("{e}"); + } + // Don't error out on this + } + for notification in connection.iter() { if stop_flag.load(Ordering::Relaxed) { log::info!("Stopping discovery!"); @@ -117,7 +138,12 @@ pub(crate) fn start_discovery( Ok(event) => { if let Event::Incoming(Packet::Publish(p)) = event { log::info!("Discovered topic={}", p.topic); - if let Err(e) = tx.send(DiscoveryMsg::Topic(p.topic)) { + let msg = if p.topic.starts_with("$SYS") { + DiscoveryMsg::SysTopic(p.topic) + } else { + DiscoveryMsg::Topic(p.topic) + }; + if let Err(e) = tx.send(msg) { log::error!("{e}"); } } diff --git a/src/mqtt_window.rs b/src/mqtt_window.rs index c03a05c5..7d6a3dbc 100644 --- a/src/mqtt_window.rs +++ b/src/mqtt_window.rs @@ -17,86 +17,56 @@ pub fn show_mqtt_window( .open(mqtt_cfg_window_open) .scroll([false, true]) .show(ctx, |ui| { - ui.group(|ui| { - ui.label("MQTT Broker Address"); - ui.horizontal(|ui| { - ui.text_edit_singleline(mqtt_cfg_window.broker_host_as_mut()) - .on_hover_text("IP address, hostname, or mDNS (.local)"); - ui.label(":"); - ui.text_edit_singleline(mqtt_cfg_window.broker_port_as_mut()) - .on_hover_text("1883 is the default MQTT broker port"); - }); - - if mqtt_cfg_window.validation_in_progress() { - ui.horizontal(|ui| { - ui.spinner(); - ui.label("Checking for broker..."); - }); - } else { - show_broker_status(ui, mqtt_cfg_window.broker_status()); - } - - mqtt_cfg_window.poll_broker_status(); - - ui.label("Topics:"); - ui.horizontal(|ui| { - ui.text_edit_singleline(mqtt_cfg_window.text_input_topic_as_mut()); - if ui.button("Add").clicked() { - mqtt_cfg_window.add_text_input_topic(); - } - }); - - let discover_enabled = mqtt_cfg_window.broker_status().reachable() - && !mqtt_cfg_window.discovery_active(); - - if !mqtt_cfg_window.discovery_active() - && ui - .add_enabled( - discover_enabled, - egui::Button::new(format!( - "{} Discover Topics", - egui_phosphor::regular::CELL_TOWER - )), - ) - .on_hover_text("Continuously find topics (subscribes to #)") - .clicked() - { - mqtt_cfg_window.start_topic_discovery(); - } - - if mqtt_cfg_window.discovery_active() { - show_active_discovery_status(ui, mqtt_cfg_window); - } - - // Display discovered topics - if !mqtt_cfg_window.discovered_topics().is_empty() { - ui.separator(); - ui.label(format!( - "Discovered Topics ({})", - mqtt_cfg_window.discovered_topics().len() - )); - - ScrollArea::vertical().max_height(800.0).show(ui, |ui| { - let topics: Vec<_> = mqtt_cfg_window.discovered_topics_sorted(); - - for topic in &topics { + ui.columns(2, |columns| { + columns[0].vertical(|ui| { + // Top section with broker config and discovery + ui.group(|ui| { + ui.label("MQTT Broker Address"); + ui.horizontal(|ui| { + ui.text_edit_singleline(mqtt_cfg_window.broker_host_as_mut()) + .on_hover_text("IP address, hostname, or mDNS (.local)"); + ui.label(":"); + ui.text_edit_singleline(mqtt_cfg_window.broker_port_as_mut()) + .on_hover_text("1883 is the default MQTT broker port"); + }); + + if mqtt_cfg_window.validation_in_progress() { ui.horizontal(|ui| { - if !mqtt_cfg_window.selected_topics_contains(topic) { - if ui.selectable_label(false, topic).clicked() { - mqtt_cfg_window.add_selected_topic(topic.to_string()); - } - } + ui.spinner(); + ui.label("Checking for broker..."); }); + } else { + show_broker_status(ui, mqtt_cfg_window.broker_status()); } + + + + mqtt_cfg_window.poll_broker_status(); + + ui.label("Topics:"); + ui.horizontal(|ui| { + ui.text_edit_singleline(mqtt_cfg_window.text_input_topic_as_mut()); + if ui.button("Add").clicked() { + mqtt_cfg_window.add_text_input_topic(); + } + }); + + show_discovered_topics_section(ui, mqtt_cfg_window); }); - } - }); - show_subscribed_topics(ui, mqtt_cfg_window); - - if ui.button("Connect").clicked() { - connect_clicked = true; - data_receiver = Some(mqtt_cfg_window.spawn_mqtt_listener()); - } + }); + // shows selected topics + columns[1].group(|ui| { + if mqtt_cfg_window.broker_status().reachable() && !mqtt_cfg_window.selected_topics().is_empty() { + ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| { + if ui.add_sized([120.0, 30.0], egui::Button::new(RichText::new("Connect").strong())).clicked() { + connect_clicked = true; + data_receiver = Some(mqtt_cfg_window.spawn_mqtt_listener()); + } + }); + } + show_subscribed_topics(ui, mqtt_cfg_window); + }); + }) }); // 4. Cleanup when window closes if (!*mqtt_cfg_window_open || connect_clicked) && mqtt_cfg_window.discovery_active() { @@ -160,9 +130,13 @@ fn show_active_discovery_status(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWin } fn show_subscribed_topics(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { - if !mqtt_cfg_window.selected_topics().is_empty() { - ui.label("Subscribed Topics:"); - } + let subscribed_topics = mqtt_cfg_window.selected_topics().len(); + let label_txt = if subscribed_topics == 0 { + "Select topics to subscribe to before connecting".into() + } else { + format!("Subscribed Topics ({subscribed_topics}):") + }; + ui.label(label_txt); for topic in mqtt_cfg_window.selected_topics_as_mut() { ui.horizontal(|ui| { if ui @@ -178,3 +152,60 @@ fn show_subscribed_topics(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { } mqtt_cfg_window.remove_empty_selected_topics(); } + +fn show_discovered_topics_list(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow, topics: Vec) { + ScrollArea::vertical().max_height(800.0).show(ui, |ui| { + for topic in &topics { + if !mqtt_cfg_window.selected_topics_contains(topic) { + ui.horizontal(|ui| { + if ui.selectable_label(false, topic).clicked() { + mqtt_cfg_window.add_selected_topic(topic.to_string()); + } + }); + } + } + }); +} + +fn show_discovered_topics_section(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { + let discover_enabled = mqtt_cfg_window.broker_status().reachable() + && !mqtt_cfg_window.discovery_active(); + + if !mqtt_cfg_window.discovery_active() + && ui + .add_enabled( + discover_enabled, + egui::Button::new(format!( + "{} Discover Topics", + egui_phosphor::regular::CELL_TOWER + )), + ) + .on_hover_text("Continuously find topics (subscribes to #)") + .clicked() + { + mqtt_cfg_window.start_topic_discovery(); + } + + if mqtt_cfg_window.discovery_active() { + show_active_discovery_status(ui, mqtt_cfg_window); + } + + // Display discovered topics + let discovered_topics = mqtt_cfg_window.discovered_topics().len(); + if discovered_topics > 0 { + ui.separator(); + ui.label(format!( + "Discovered Topics ({discovered_topics})" + )); + + show_discovered_topics_list(ui, mqtt_cfg_window, mqtt_cfg_window.discovered_topics_sorted()); + } + + let discovered_sys_topics = mqtt_cfg_window.discovered_sys_topics().len(); + if discovered_sys_topics > 0 { + ui.collapsing(format!("Broker sys topics ({discovered_sys_topics})"), |ui| { + ui.separator(); + show_discovered_topics_list(ui, mqtt_cfg_window, mqtt_cfg_window.discovered_sys_topics_sorted()); + }); + } +} From 12adeb3ef22e61fddfda9587a2e014b16b81c0ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 19:21:31 +0200 Subject: [PATCH 19/48] refactor and improve font colors --- src/mqtt_window.rs | 171 ++++++++++++++++++++++++++++----------------- src/util.rs | 9 +++ 2 files changed, 116 insertions(+), 64 deletions(-) diff --git a/src/mqtt_window.rs b/src/mqtt_window.rs index 7d6a3dbc..b0573995 100644 --- a/src/mqtt_window.rs +++ b/src/mqtt_window.rs @@ -5,6 +5,8 @@ use egui::Ui; use plotinator_mqtt::BrokerStatus; use plotinator_mqtt::{MqttConfigWindow, MqttDataReceiver}; +use crate::util::theme_color; + /// Shows the MQTT configuration window and returns a receiver channel if connect was clicked pub fn show_mqtt_window( ctx: &egui::Context, @@ -18,55 +20,14 @@ pub fn show_mqtt_window( .scroll([false, true]) .show(ctx, |ui| { ui.columns(2, |columns| { - columns[0].vertical(|ui| { - // Top section with broker config and discovery - ui.group(|ui| { - ui.label("MQTT Broker Address"); - ui.horizontal(|ui| { - ui.text_edit_singleline(mqtt_cfg_window.broker_host_as_mut()) - .on_hover_text("IP address, hostname, or mDNS (.local)"); - ui.label(":"); - ui.text_edit_singleline(mqtt_cfg_window.broker_port_as_mut()) - .on_hover_text("1883 is the default MQTT broker port"); - }); - - if mqtt_cfg_window.validation_in_progress() { - ui.horizontal(|ui| { - ui.spinner(); - ui.label("Checking for broker..."); - }); - } else { - show_broker_status(ui, mqtt_cfg_window.broker_status()); - } - - - - mqtt_cfg_window.poll_broker_status(); - - ui.label("Topics:"); - ui.horizontal(|ui| { - ui.text_edit_singleline(mqtt_cfg_window.text_input_topic_as_mut()); - if ui.button("Add").clicked() { - mqtt_cfg_window.add_text_input_topic(); - } - }); - - show_discovered_topics_section(ui, mqtt_cfg_window); - }); - }); - // shows selected topics - columns[1].group(|ui| { - if mqtt_cfg_window.broker_status().reachable() && !mqtt_cfg_window.selected_topics().is_empty() { - ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| { - if ui.add_sized([120.0, 30.0], egui::Button::new(RichText::new("Connect").strong())).clicked() { - connect_clicked = true; - data_receiver = Some(mqtt_cfg_window.spawn_mqtt_listener()); - } - }); - } - show_subscribed_topics(ui, mqtt_cfg_window); - }); - }) + show_broker_config_column(&mut columns[0], mqtt_cfg_window); + show_subscribed_topics_column( + &mut columns[1], + mqtt_cfg_window, + &mut connect_clicked, + &mut data_receiver, + ); + }); }); // 4. Cleanup when window closes if (!*mqtt_cfg_window_open || connect_clicked) && mqtt_cfg_window.discovery_active() { @@ -75,6 +36,68 @@ pub fn show_mqtt_window( data_receiver } +fn show_broker_config_column(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { + ui.group(|ui| { + ui.set_min_width(ui.available_width() - 200.0); // Reserve space for right column + ui.label("MQTT Broker Address"); + ui.horizontal(|ui| { + ui.text_edit_singleline(mqtt_cfg_window.broker_host_as_mut()) + .on_hover_text("IP address, hostname, or mDNS (.local)"); + ui.label(":"); + ui.text_edit_singleline(mqtt_cfg_window.broker_port_as_mut()) + .on_hover_text("1883 is the default MQTT broker port"); + }); + + if mqtt_cfg_window.validation_in_progress() { + ui.horizontal(|ui| { + ui.spinner(); + ui.label("Checking for broker..."); + }); + } else { + show_broker_status(ui, mqtt_cfg_window.broker_status()); + } + + mqtt_cfg_window.poll_broker_status(); + + ui.label("Topics:"); + ui.horizontal(|ui| { + ui.text_edit_singleline(mqtt_cfg_window.text_input_topic_as_mut()); + if ui.button("Add").clicked() { + mqtt_cfg_window.add_text_input_topic(); + } + }); + + show_discovered_topics_section(ui, mqtt_cfg_window); + }); +} + +fn show_subscribed_topics_column( + ui: &mut Ui, + mqtt_cfg_window: &mut MqttConfigWindow, + connect_clicked: &mut bool, + data_receiver: &mut Option, +) { + ui.group(|ui| { + ui.set_min_width(200.0); + let is_connect_valid = mqtt_cfg_window.broker_status().reachable() + && !mqtt_cfg_window.selected_topics().is_empty(); + ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| { + if ui + .add_enabled( + is_connect_valid, + egui::Button::new(RichText::new("Connect").strong()) + .min_size([120.0, 30.0].into()), + ) + .clicked() + { + *connect_clicked = true; + *data_receiver = Some(mqtt_cfg_window.spawn_mqtt_listener()); + } + }); + show_subscribed_topics(ui, mqtt_cfg_window); + }); +} + fn show_broker_status(ui: &mut Ui, broker_status: &BrokerStatus) { match broker_status { BrokerStatus::None => (), @@ -120,7 +143,10 @@ fn show_active_discovery_status(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWin // Show discovery status ui.horizontal(|ui| { ui.spinner(); - ui.colored_label(Color32::BLUE, "Discovering topics..."); + ui.colored_label( + theme_color(ui, Color32::CYAN, Color32::BLUE), + "Discovering topics...", + ); }); // Process incoming topics @@ -130,11 +156,15 @@ fn show_active_discovery_status(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWin } fn show_subscribed_topics(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { - let subscribed_topics = mqtt_cfg_window.selected_topics().len(); + let subscribed_topics = mqtt_cfg_window.selected_topics().len(); let label_txt = if subscribed_topics == 0 { - "Select topics to subscribe to before connecting".into() + RichText::new("Select topics to subscribe to before connecting").color(theme_color( + ui, + Color32::YELLOW, + Color32::ORANGE, + )) } else { - format!("Subscribed Topics ({subscribed_topics}):") + RichText::new(format!("Subscribed Topics ({subscribed_topics}):")) }; ui.label(label_txt); for topic in mqtt_cfg_window.selected_topics_as_mut() { @@ -153,7 +183,11 @@ fn show_subscribed_topics(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { mqtt_cfg_window.remove_empty_selected_topics(); } -fn show_discovered_topics_list(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow, topics: Vec) { +fn show_discovered_topics_list( + ui: &mut Ui, + mqtt_cfg_window: &mut MqttConfigWindow, + topics: Vec, +) { ScrollArea::vertical().max_height(800.0).show(ui, |ui| { for topic in &topics { if !mqtt_cfg_window.selected_topics_contains(topic) { @@ -168,8 +202,8 @@ fn show_discovered_topics_list(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWind } fn show_discovered_topics_section(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { - let discover_enabled = mqtt_cfg_window.broker_status().reachable() - && !mqtt_cfg_window.discovery_active(); + let discover_enabled = + mqtt_cfg_window.broker_status().reachable() && !mqtt_cfg_window.discovery_active(); if !mqtt_cfg_window.discovery_active() && ui @@ -194,18 +228,27 @@ fn show_discovered_topics_section(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigW let discovered_topics = mqtt_cfg_window.discovered_topics().len(); if discovered_topics > 0 { ui.separator(); - ui.label(format!( - "Discovered Topics ({discovered_topics})" - )); + ui.label(format!("Discovered Topics ({discovered_topics})")); - show_discovered_topics_list(ui, mqtt_cfg_window, mqtt_cfg_window.discovered_topics_sorted()); + show_discovered_topics_list( + ui, + mqtt_cfg_window, + mqtt_cfg_window.discovered_topics_sorted(), + ); } let discovered_sys_topics = mqtt_cfg_window.discovered_sys_topics().len(); if discovered_sys_topics > 0 { - ui.collapsing(format!("Broker sys topics ({discovered_sys_topics})"), |ui| { - ui.separator(); - show_discovered_topics_list(ui, mqtt_cfg_window, mqtt_cfg_window.discovered_sys_topics_sorted()); - }); + ui.collapsing( + format!("Broker sys topics ({discovered_sys_topics})"), + |ui| { + ui.separator(); + show_discovered_topics_list( + ui, + mqtt_cfg_window, + mqtt_cfg_window.discovered_sys_topics_sorted(), + ); + }, + ); } } diff --git a/src/util.rs b/src/util.rs index b2718e18..433787c1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,7 @@ use std::{ops::RangeInclusive, time::Duration}; use chrono::{DateTime, Timelike as _}; +use egui::{Color32, Ui}; use egui_plot::{GridMark, PlotPoint}; /// Format a timestamp in milliseconds into `HH:MM:SS.ms` @@ -127,3 +128,11 @@ pub fn format_data_size(size_bytes: usize) -> String { } } } + +/// Selects between the colors based on the current UI theme +pub(crate) fn theme_color(ui: &Ui, dark: Color32, light: Color32) -> Color32 { + match ui.ctx().theme() { + egui::Theme::Dark => dark, + egui::Theme::Light => light, + } +} From 397edc75e70d14ebea9603a47507c8f9ab2c5d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 19:24:39 +0200 Subject: [PATCH 20/48] fix lints --- Cargo.toml | 1 - crates/plotinator_mqtt/src/broker_validator.rs | 14 +++++++------- src/mqtt_window.rs | 8 ++++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aa70d004..8d116949 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -171,7 +171,6 @@ clear_with_drain = "warn" cloned_instead_of_copied = "warn" dbg_macro = "warn" debug_assert_with_mut_call = "warn" -derive_partial_eq_without_eq = "warn" doc_link_with_quotes = "warn" doc_markdown = "warn" empty_enum = "warn" diff --git a/crates/plotinator_mqtt/src/broker_validator.rs b/crates/plotinator_mqtt/src/broker_validator.rs index 120345e7..4b659b37 100644 --- a/crates/plotinator_mqtt/src/broker_validator.rs +++ b/crates/plotinator_mqtt/src/broker_validator.rs @@ -6,7 +6,7 @@ use std::{ use rumqttc::{Client, Event, MqttOptions, Packet}; use crate::util::timestamped_client_id; -#[derive(Debug, Default, Clone, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub enum BrokerStatus { #[default] None, @@ -18,10 +18,10 @@ pub enum BrokerStatus { impl BrokerStatus { pub fn reachable(&self) -> bool { match self { - BrokerStatus::Reachable | - BrokerStatus::ReachableVersion(_) => true, - BrokerStatus::Unreachable(_) | - BrokerStatus::None => false, + Self::Reachable | + Self::ReachableVersion(_) => true, + Self::Unreachable(_) | + Self::None => false, } } } @@ -176,9 +176,9 @@ fn get_broker_version(addr: SocketAddr) -> Result { } Some(Err(e)) => return Err(format!("Connection error: {e}")), None => break, - _ => continue, + _ => (), } } - Err("Timeout waiting for broker version".to_string()) + Err("Timeout waiting for broker version".to_owned()) } diff --git a/src/mqtt_window.rs b/src/mqtt_window.rs index b0573995..0d7a0409 100644 --- a/src/mqtt_window.rs +++ b/src/mqtt_window.rs @@ -186,10 +186,10 @@ fn show_subscribed_topics(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { fn show_discovered_topics_list( ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow, - topics: Vec, + topics: &[String], ) { ScrollArea::vertical().max_height(800.0).show(ui, |ui| { - for topic in &topics { + for topic in topics { if !mqtt_cfg_window.selected_topics_contains(topic) { ui.horizontal(|ui| { if ui.selectable_label(false, topic).clicked() { @@ -233,7 +233,7 @@ fn show_discovered_topics_section(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigW show_discovered_topics_list( ui, mqtt_cfg_window, - mqtt_cfg_window.discovered_topics_sorted(), + &mqtt_cfg_window.discovered_topics_sorted(), ); } @@ -246,7 +246,7 @@ fn show_discovered_topics_section(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigW show_discovered_topics_list( ui, mqtt_cfg_window, - mqtt_cfg_window.discovered_sys_topics_sorted(), + &mqtt_cfg_window.discovered_sys_topics_sorted(), ); }, ); From 6d736b6546f2209ec867d4185841ab48ca4dbb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 19:27:47 +0200 Subject: [PATCH 21/48] ignore example code --- crates/plotinator_macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/plotinator_macros/src/lib.rs b/crates/plotinator_macros/src/lib.rs index bbd18f7e..a13e4bfc 100644 --- a/crates/plotinator_macros/src/lib.rs +++ b/crates/plotinator_macros/src/lib.rs @@ -6,7 +6,7 @@ /// /// # Examples /// -/// ``` +/// ```ignore /// // Declare multiple modules that will only be included in non-WebAssembly builds /// non_wasm_modules!( /// // Public module From 891610d5c36e663a8ef0b1669a8cef5fe3ae86b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 19:35:45 +0200 Subject: [PATCH 22/48] tweak --- src/plot/plot_settings.rs | 8 +++++++- src/util.rs | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plot/plot_settings.rs b/src/plot/plot_settings.rs index 45a338b1..cc789727 100644 --- a/src/plot/plot_settings.rs +++ b/src/plot/plot_settings.rs @@ -7,6 +7,8 @@ use plot_util::{MipMapConfiguration, PlotValues, Plots}; use plot_visibility_config::PlotVisibilityConfig; use serde::{Deserialize, Serialize}; +use crate::util::theme_color; + pub mod date_settings; mod loaded_logs; pub mod mipmap_settings; @@ -63,7 +65,11 @@ pub struct PlotSettings { impl PlotSettings { pub fn show(&mut self, ui: &mut egui::Ui) { if self.loaded_log_settings.is_empty() { - ui.label(RichText::new("No Files Loaded").color(Color32::RED)); + ui.label(RichText::new("No Files Loaded").color(theme_color( + ui, + Color32::RED, + Color32::DARK_RED, + ))); } else { self.show_loaded_files(ui); self.ui_plot_filter_settings(ui); diff --git a/src/util.rs b/src/util.rs index 433787c1..7bfd2448 100644 --- a/src/util.rs +++ b/src/util.rs @@ -130,6 +130,7 @@ pub fn format_data_size(size_bytes: usize) -> String { } /// Selects between the colors based on the current UI theme +#[must_use] pub(crate) fn theme_color(ui: &Ui, dark: Color32, light: Color32) -> Color32 { match ui.ctx().theme() { egui::Theme::Dark => dark, From 8256bf3f44307ac9e88fee08bb12395a67db09a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 19:44:50 +0200 Subject: [PATCH 23/48] entry --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ae13054..0945dfd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +### Added + +- Discover broker `$SYS`-topics +- Show reachable broker's version if available +- Better UX for the MQTT connection window + ### Dependencies - `anyhow`: 1.0.97 → 1.0.98 ([#228](https://github.com/luftkode/plotinator3000/pull/228)) From 595edf7869bf1b81f870637acb0bdee86af72da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sat, 10 May 2025 19:51:53 +0200 Subject: [PATCH 24/48] use mimalloc as the global allocator --- CHANGELOG.md | 4 ++++ Cargo.lock | 20 ++++++++++++++++++++ Cargo.toml | 1 + Justfile | 2 +- src/main.rs | 3 +++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0945dfd1..318d6624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Show reachable broker's version if available - Better UX for the MQTT connection window +### Changed + +- Switch to `mimalloc` as the global allocator for significant performance improvements (~20%) + ### Dependencies - `anyhow`: 1.0.97 → 1.0.98 ([#228](https://github.com/luftkode/plotinator3000/pull/228)) diff --git a/Cargo.lock b/Cargo.lock index 1d9257bf..fe69d278 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2396,6 +2396,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "libmimalloc-sys" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "libredox" version = "0.1.3" @@ -2570,6 +2580,15 @@ dependencies = [ "syn", ] +[[package]] +name = "mimalloc" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af" +dependencies = [ + "libmimalloc-sys", +] + [[package]] name = "mime" version = "0.3.17" @@ -3246,6 +3265,7 @@ dependencies = [ "getset", "log", "log_if", + "mimalloc", "plot_util", "plotinator_macros", "plotinator_mqtt", diff --git a/Cargo.toml b/Cargo.toml index 8d116949..62d5c20b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,6 +94,7 @@ rfd = "0.15" egui-phosphor = "0.9.0" egui-notify = "0.19.0" tokio = "1.44.2" +mimalloc = "0.1.46" [dev-dependencies] testresult.workspace = true diff --git a/Justfile b/Justfile index c402d066..63c1a1d8 100644 --- a/Justfile +++ b/Justfile @@ -96,7 +96,7 @@ audit *ARGS: [group("Profiling")] run-profiling *ARGS: cargo install puffin_viewer --locked - cargo {{run}} --features profiling -- {{ARGS}} + cargo {{run}} --features profiling {{ARGS}} # Requires firebase CLI and access to MKI firebase account [group("Web")] diff --git a/src/main.rs b/src/main.rs index bcb529d4..ccfb5e03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,9 @@ #![warn(clippy::all, rust_2018_idioms)] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release +#[global_allocator] +static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; // Much faster allocator, can give 20% speedups + // When compiling natively: #[cfg(not(target_arch = "wasm32"))] fn main() -> eframe::Result { From 563acfea6e8b61a9932b68d06f9cb853219bda8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 16:51:52 +0200 Subject: [PATCH 25/48] readability --- src/app.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app.rs b/src/app.rs index 9dd21c7b..3d51d883 100644 --- a/src/app.rs +++ b/src/app.rs @@ -267,8 +267,8 @@ fn show_top_panel(app: &mut App, ctx: &egui::Context) { } if ui .button(RichText::new(format!( - "{} Open File", - egui_phosphor::regular::FOLDER_OPEN + "{icon} Open File", + icon = egui_phosphor::regular::FOLDER_OPEN ))) .clicked() { From a7661592a790ebd461f587904c1fd25d59c238f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 16:52:00 +0200 Subject: [PATCH 26/48] remove bad min_width setting --- src/mqtt_window.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mqtt_window.rs b/src/mqtt_window.rs index 0d7a0409..9e54e77c 100644 --- a/src/mqtt_window.rs +++ b/src/mqtt_window.rs @@ -38,7 +38,6 @@ pub fn show_mqtt_window( fn show_broker_config_column(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { ui.group(|ui| { - ui.set_min_width(ui.available_width() - 200.0); // Reserve space for right column ui.label("MQTT Broker Address"); ui.horizontal(|ui| { ui.text_edit_singleline(mqtt_cfg_window.broker_host_as_mut()) @@ -78,7 +77,6 @@ fn show_subscribed_topics_column( data_receiver: &mut Option, ) { ui.group(|ui| { - ui.set_min_width(200.0); let is_connect_valid = mqtt_cfg_window.broker_status().reachable() && !mqtt_cfg_window.selected_topics().is_empty(); ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| { From 9919b783135be56899807bec40d0cc24ddd530a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 18:21:43 +0200 Subject: [PATCH 27/48] fix lints --- crates/plotinator_mqtt/src/parse_packet/known_topic.rs | 4 ++-- crates/skytem_logs/src/mbed_motor_control/pid/header/v1.rs | 2 +- crates/skytem_logs/src/mbed_motor_control/pid/header/v2.rs | 2 +- crates/skytem_logs/src/mbed_motor_control/pid/header/v3.rs | 2 +- crates/skytem_logs/src/mbed_motor_control/status/header/v2.rs | 2 +- crates/skytem_logs/src/mbed_motor_control/status/header/v3.rs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/plotinator_mqtt/src/parse_packet/known_topic.rs b/crates/plotinator_mqtt/src/parse_packet/known_topic.rs index c517998f..62163ff4 100644 --- a/crates/plotinator_mqtt/src/parse_packet/known_topic.rs +++ b/crates/plotinator_mqtt/src/parse_packet/known_topic.rs @@ -39,7 +39,7 @@ pub(crate) enum KnownTopic { SYSBrokerUptime, // We cannot meaningfully plot this, but we use it to show the version when choosing a broker to connect to #[strum(serialize = "$SYS/broker/version")] - SYSBrokerVersion + SYSBrokerVersion, } /// Debug packet with a single value @@ -155,7 +155,7 @@ impl KnownTopic { #[cfg(test)] mod tests { - use std::str::FromStr; + use std::str::FromStr as _; use super::*; use serde_json::json; diff --git a/crates/skytem_logs/src/mbed_motor_control/pid/header/v1.rs b/crates/skytem_logs/src/mbed_motor_control/pid/header/v1.rs index 83c8c605..ad933bfd 100644 --- a/crates/skytem_logs/src/mbed_motor_control/pid/header/v1.rs +++ b/crates/skytem_logs/src/mbed_motor_control/pid/header/v1.rs @@ -156,7 +156,7 @@ impl fmt::Display for PidLogHeaderV1 { #[cfg(test)] mod tests { use super::*; - use io::Read; + use io::Read as _; use test_util::*; #[test] diff --git a/crates/skytem_logs/src/mbed_motor_control/pid/header/v2.rs b/crates/skytem_logs/src/mbed_motor_control/pid/header/v2.rs index 66af596f..81c0e664 100644 --- a/crates/skytem_logs/src/mbed_motor_control/pid/header/v2.rs +++ b/crates/skytem_logs/src/mbed_motor_control/pid/header/v2.rs @@ -170,7 +170,7 @@ mod tests { use super::*; use test_util::*; - use io::Read; + use io::Read as _; #[test] fn test_deserialize() -> TestResult { diff --git a/crates/skytem_logs/src/mbed_motor_control/pid/header/v3.rs b/crates/skytem_logs/src/mbed_motor_control/pid/header/v3.rs index e19b51c1..5a18d041 100644 --- a/crates/skytem_logs/src/mbed_motor_control/pid/header/v3.rs +++ b/crates/skytem_logs/src/mbed_motor_control/pid/header/v3.rs @@ -168,7 +168,7 @@ impl fmt::Display for PidLogHeaderV3 { #[cfg(test)] mod tests { use super::*; - use io::Read; + use io::Read as _; use test_util::*; #[test] diff --git a/crates/skytem_logs/src/mbed_motor_control/status/header/v2.rs b/crates/skytem_logs/src/mbed_motor_control/status/header/v2.rs index 6eb86998..24d1003f 100644 --- a/crates/skytem_logs/src/mbed_motor_control/status/header/v2.rs +++ b/crates/skytem_logs/src/mbed_motor_control/status/header/v2.rs @@ -166,7 +166,7 @@ mod tests { use super::*; use test_util::*; - use crate::mbed_motor_control::mbed_config::MbedConfig; + use crate::mbed_motor_control::mbed_config::MbedConfig as _; #[test] fn test_deserialize() -> TestResult { diff --git a/crates/skytem_logs/src/mbed_motor_control/status/header/v3.rs b/crates/skytem_logs/src/mbed_motor_control/status/header/v3.rs index bc961d77..30fde13c 100644 --- a/crates/skytem_logs/src/mbed_motor_control/status/header/v3.rs +++ b/crates/skytem_logs/src/mbed_motor_control/status/header/v3.rs @@ -166,7 +166,7 @@ mod tests { use super::*; use test_util::*; - use crate::mbed_motor_control::mbed_config::MbedConfig; + use crate::mbed_motor_control::mbed_config::MbedConfig as _; #[test] fn test_deserialize() -> TestResult { From fcc281049581f1873007ea2202bbaf2a0cc1aa19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 18:22:02 +0200 Subject: [PATCH 28/48] don't use mimalloc on wasm --- Cargo.toml | 2 +- src/main.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 62d5c20b..539c0689 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,7 +94,6 @@ rfd = "0.15" egui-phosphor = "0.9.0" egui-notify = "0.19.0" tokio = "1.44.2" -mimalloc = "0.1.46" [dev-dependencies] testresult.workspace = true @@ -106,6 +105,7 @@ reqwest.workspace = true env_logger = "0.11" zip = "2.6.1" axoupdater = { version = "0.9.0", features = ["blocking"], optional = true} +mimalloc = "0.1.46" plotinator_mqtt = { workspace = true, optional = true} profiling = { workspace = true, optional = true} puffin = { workspace = true, optional = true} diff --git a/src/main.rs b/src/main.rs index ccfb5e03..af9f7902 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ #![warn(clippy::all, rust_2018_idioms)] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release +#[cfg(not(target_arch = "wasm32"))] #[global_allocator] -static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; // Much faster allocator, can give 20% speedups +static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; // Much faster allocator, frames rendered ~25% faster on windows 11 // When compiling natively: #[cfg(not(target_arch = "wasm32"))] From 33618c32ad94e05afec940be66c4feca3f6210cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 18:22:21 +0200 Subject: [PATCH 29/48] encapsulate MQTT UI more --- src/app.rs | 69 +++++-------------------- src/lib.rs | 2 +- src/mqtt.rs | 96 +++++++++++++++++++++++++++++++++++ src/{ => mqtt}/mqtt_window.rs | 0 4 files changed, 111 insertions(+), 56 deletions(-) create mode 100644 src/mqtt.rs rename src/{ => mqtt}/mqtt_window.rs (100%) diff --git a/src/app.rs b/src/app.rs index 3d51d883..fd13b7c6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -28,10 +28,6 @@ pub struct App { #[serde(skip)] toasts: Toasts, - // auto scale plot bounds (MQTT only) - #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - pub set_auto_bounds: bool, - loaded_files: LoadedFiles, plot: LogPlotUi, font_size: f32, @@ -40,12 +36,7 @@ pub struct App { #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] #[serde(skip)] - mqtt_data_receiver: Option, - #[serde(skip)] - #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - mqtt_config_window: Option, - #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - mqtt_cfg_window_open: bool, + mqtt: crate::mqtt::Mqtt, #[cfg(target_arch = "wasm32")] #[serde(skip)] @@ -70,13 +61,7 @@ impl Default for App { error_message: None, #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - mqtt_config_window: None, - #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - mqtt_cfg_window_open: false, - #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - set_auto_bounds: false, - #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - mqtt_data_receiver: None, + mqtt: crate::mqtt::Mqtt::default(), #[cfg(target_arch = "wasm32")] web_file_dialog: fd::web::WebFileDialog::default(), @@ -148,26 +133,15 @@ impl eframe::App for App { &self.loaded_files.take_loaded_files(), &mut self.toasts, #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - self.mqtt_data_receiver - .as_ref() - .map(|mdc| mdc.plots()) - .unwrap_or_default(), + crate::mqtt::Mqtt::plots(self.mqtt.mqtt_data_receiver.as_ref()), #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - &mut self.set_auto_bounds, + &mut self.mqtt.set_auto_bounds, ); #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - if let Some(mqtt_receiver) = self.mqtt_data_receiver.as_ref() { - if mqtt_receiver.plots().is_empty() { - ui.add_space(20.); - ui.vertical_centered_justified(|ui| { - ui.heading("Waiting for data on any of the following topics:"); - for topic in mqtt_receiver.subscribed_topics() { - ui.label(topic); - } - ui.spinner(); - }); - } + { + ui.add_space(20.); + self.mqtt.show_waiting_for_initial_data(ui); } if self.plot.plot_count() == 0 { // Display the message when plots are shown @@ -242,8 +216,8 @@ fn show_top_panel(app: &mut App, ctx: &egui::Context) { egui::menu::bar(ui, |ui| { if ui .button(RichText::new(format!( - "{} Reset", - egui_phosphor::regular::TRASH + "{icon} Reset", + icon = egui_phosphor::regular::TRASH ))) .clicked() { @@ -260,10 +234,7 @@ fn show_top_panel(app: &mut App, ctx: &egui::Context) { app.plot = LogPlotUi::default(); #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - { - app.mqtt_config_window = None; - app.mqtt_data_receiver = None; - } + app.mqtt.reset(); } if ui .button(RichText::new(format!( @@ -306,27 +277,15 @@ fn show_top_panel(app: &mut App, ctx: &egui::Context) { #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] { if ui.button("MQTT connect").clicked() { - app.mqtt_config_window = Some(plotinator_mqtt::MqttConfigWindow::default()); - app.mqtt_cfg_window_open = true; + app.mqtt.connect(); } - if let Some(data_receiver) = &mut app.mqtt_data_receiver { - data_receiver.poll(); + if app.mqtt.listener_active() { + app.mqtt.poll_data(); ctx.request_repaint_after(Duration::from_millis(50)); } // Show MQTT configuration window if needed - if app.mqtt_data_receiver.is_none() { - if let Some(config) = &mut app.mqtt_config_window { - if let Some(data_receiver) = crate::mqtt_window::show_mqtt_window( - ctx, - &mut app.mqtt_cfg_window_open, - config, - ) { - app.mqtt_data_receiver = Some(data_receiver); - app.set_auto_bounds = true; - } - } - } + app.mqtt.show_connect_window(ctx); } collapsible_instructions(ui); }); diff --git a/src/lib.rs b/src/lib.rs index 98238bd5..c65c62eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ pub fn get_app_version() -> &'static Version { } #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] -pub mod mqtt_window; +pub mod mqtt; pub mod plot; #[cfg(all(feature = "profiling", not(target_arch = "wasm32")))] pub mod profiling; diff --git a/src/mqtt.rs b/src/mqtt.rs new file mode 100644 index 00000000..e226f08a --- /dev/null +++ b/src/mqtt.rs @@ -0,0 +1,96 @@ +use plotinator_mqtt::{MqttConfigWindow, MqttDataReceiver, MqttPlotPoints}; + +pub mod mqtt_window; + +#[derive(Default)] +pub struct Mqtt { + pub mqtt_data_receiver: Option, + mqtt_config_window: Option, + mqtt_cfg_window_open: bool, + // auto scale plot bounds (MQTT only) + pub set_auto_bounds: bool, +} + +impl Mqtt { + pub fn reset(&mut self) { + self.mqtt_data_receiver = None; + self.mqtt_config_window = None; + } + + pub fn connect(&mut self) { + self.mqtt_config_window = Some(plotinator_mqtt::MqttConfigWindow::default()); + self.mqtt_cfg_window_open = true; + } + + pub fn poll_data(&mut self) { + self.mqtt_data_receiver + .as_mut() + .expect("Attempted to poll when no listener is active") + .poll(); + } + + pub fn window_open(&self) -> bool { + self.mqtt_cfg_window_open + } + + pub fn window_open_mut(&mut self) -> &mut bool { + &mut self.mqtt_cfg_window_open + } + + pub fn plots(mqtt_data_receiver: Option<&MqttDataReceiver>) -> &[MqttPlotPoints] { + mqtt_data_receiver + .as_ref() + .map(|mdc| mdc.plots()) + .unwrap_or_default() + } + + pub fn listener_active(&self) -> bool { + self.mqtt_data_receiver.is_some() + } + + /// Returns true if we're listening for MQTT data but have yet to receive enough to display a plot + /// + /// One topic needs at least 2 points for us to have anything to plot + fn waiting_for_initial_data(&self) -> bool { + if let Some(r) = &self.mqtt_data_receiver { + for p in r.plots() { + if p.data.len() > 1 { + // A topic has more than 1 plot point so we are no longer waiting + return false; + } + } + // We are receiving MQTT data but no topic has 2 plot points or more + return true; + } + // We are not receiving MQTT data, so we are not waiting for it + false + } + + pub fn show_waiting_for_initial_data(&self, ui: &mut egui::Ui) { + if self.waiting_for_initial_data() { + ui.vertical_centered_justified(|ui| { + ui.heading("Waiting for data on any of the following topics:"); + debug_assert!(self.mqtt_data_receiver.is_some(), "Expected an active MQTT data receiver when painting 'waiting for initial data' elements"); + for topic in self.mqtt_data_receiver.as_ref().expect("Unsound condition").subscribed_topics() { + ui.label(topic); + } + ui.spinner(); + }); + } + } + + pub fn show_connect_window(&mut self, ctx: &egui::Context) { + if self.mqtt_data_receiver.is_none() { + if let Some(config) = &mut self.mqtt_config_window { + if let Some(data_receiver) = crate::mqtt::mqtt_window::show_mqtt_window( + ctx, + &mut self.mqtt_cfg_window_open, + config, + ) { + self.mqtt_data_receiver = Some(data_receiver); + self.set_auto_bounds = true; + } + } + } + } +} diff --git a/src/mqtt_window.rs b/src/mqtt/mqtt_window.rs similarity index 100% rename from src/mqtt_window.rs rename to src/mqtt/mqtt_window.rs From 6ac248a85b165bd81978a160414f9516bf4ddc04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 18:39:49 +0200 Subject: [PATCH 30/48] more informative when waiting for MQTT data points --- src/app.rs | 9 +-------- src/mqtt.rs | 3 ++- src/plot.rs | 16 +++++++++------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/app.rs b/src/app.rs index fd13b7c6..d7f47dc0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -133,16 +133,9 @@ impl eframe::App for App { &self.loaded_files.take_loaded_files(), &mut self.toasts, #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - crate::mqtt::Mqtt::plots(self.mqtt.mqtt_data_receiver.as_ref()), - #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - &mut self.mqtt.set_auto_bounds, + &mut self.mqtt, ); - #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - { - ui.add_space(20.); - self.mqtt.show_waiting_for_initial_data(ui); - } if self.plot.plot_count() == 0 { // Display the message when plots are shown util::draw_empty_state(ui); diff --git a/src/mqtt.rs b/src/mqtt.rs index e226f08a..57fd3dd5 100644 --- a/src/mqtt.rs +++ b/src/mqtt.rs @@ -68,8 +68,9 @@ impl Mqtt { pub fn show_waiting_for_initial_data(&self, ui: &mut egui::Ui) { if self.waiting_for_initial_data() { + ui.add_space(20.); ui.vertical_centered_justified(|ui| { - ui.heading("Waiting for data on any of the following topics:"); + ui.heading("Waiting for 2 data points on any of the following topics:"); debug_assert!(self.mqtt_data_receiver.is_some(), "Expected an active MQTT data receiver when painting 'waiting for initial data' elements"); for topic in self.mqtt_data_receiver.as_ref().expect("Unsound condition").subscribed_topics() { ui.label(topic); diff --git a/src/plot.rs b/src/plot.rs index eeb783a1..a79f306a 100644 --- a/src/plot.rs +++ b/src/plot.rs @@ -75,9 +75,7 @@ impl LogPlotUi { ui: &mut egui::Ui, loaded_files: &[SupportedFormat], toasts: &mut Toasts, - #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - mqtt_plots: &[plotinator_mqtt::MqttPlotPoints], - #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] set_auto_bounds: &mut bool, + #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] mqtt: &mut crate::mqtt::Mqtt, ) -> Response { #[cfg(all(feature = "profiling", not(target_arch = "wasm32")))] puffin::profile_scope!("Plot_UI"); @@ -129,10 +127,14 @@ impl LogPlotUi { plot_settings.refresh(plots); #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - let mode = if mqtt_plots.is_empty() { - PlotMode::Logs(plots) - } else { - PlotMode::MQTT(mqtt_plots, set_auto_bounds) + let mode = { + mqtt.show_waiting_for_initial_data(ui); + let mqtt_plots = crate::mqtt::Mqtt::plots(mqtt.mqtt_data_receiver.as_ref()); + if mqtt_plots.is_empty() { + PlotMode::Logs(plots) + } else { + PlotMode::MQTT(mqtt_plots, &mut mqtt.set_auto_bounds) + } }; #[cfg(not(all(not(target_arch = "wasm32"), feature = "mqtt")))] let mode = PlotMode::Logs(plots); From c8a04804b26f49c574d56de269fafd0c2696fb89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 18:40:16 +0200 Subject: [PATCH 31/48] reduce verbosity of noisy logs --- src/plot/util.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plot/util.rs b/src/plot/util.rs index a42609a8..b2e866e1 100644 --- a/src/plot/util.rs +++ b/src/plot/util.rs @@ -93,13 +93,13 @@ pub fn set_zoom_factor(scroll: Vec2, modifiers: Modifiers) -> Option { let ctrl_plus_alt = modifiers.alt && ctrl; if ctrl_plus_alt { - log::info!("CTRL+ALT locks X-axis"); + log::debug!("CTRL+ALT locks X-axis"); zoom_factor.x = 1.0; } else if ctrl { - log::info!("CTRL locks Y-axis"); + log::debug!("CTRL locks Y-axis"); zoom_factor.y = 1.0; } - log::info!("zoom_factor={zoom_factor}"); + log::debug!("zoom_factor={zoom_factor}"); if ctrl { Some(zoom_factor) } else { None } } From b2ceebfe96e64921491d36ef82a98152b98897fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 21:37:02 +0200 Subject: [PATCH 32/48] tweak --- src/plot/plot_ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plot/plot_ui.rs b/src/plot/plot_ui.rs index 99c1b201..c531ebfd 100644 --- a/src/plot/plot_ui.rs +++ b/src/plot/plot_ui.rs @@ -13,7 +13,7 @@ pub fn show_settings_grid( ui.horizontal_wrapped(|ui| { plot_settings.show(ui); ui.label("|"); - let axis_cfg_str = RichText::new(format!("{} Axis config", regular::GEAR)); + let axis_cfg_str = RichText::new(format!("{icon} Axis", icon = regular::GEAR)); if ui.button(axis_cfg_str.clone()).clicked() { axis_cfg.ui_visible = !axis_cfg.ui_visible; } From e3687ce5416d3d4646741cc4a504e7ee4ef8ab3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 21:38:52 +0200 Subject: [PATCH 33/48] add GUI and GUI snapshot tests with egui_kittest --- .gitignore | 5 +- Cargo.lock | 420 +++++++++++++++++- Cargo.toml | 3 + README.md | 4 + tests/snapshots/default_app_window.png | Bin 0 -> 122904 bytes .../snapshots/default_mqtt_config_window.png | Bin 0 -> 55149 bytes .../snapshots/dropped_mbed_pid_regular_v6.png | Bin 0 -> 140652 bytes .../dropped_mbed_status_regular_v6.png | Bin 0 -> 97647 bytes tests/test_p3k.rs | 139 ++++++ 9 files changed, 558 insertions(+), 13 deletions(-) create mode 100644 tests/snapshots/default_app_window.png create mode 100644 tests/snapshots/default_mqtt_config_window.png create mode 100644 tests/snapshots/dropped_mbed_pid_regular_v6.png create mode 100644 tests/snapshots/dropped_mbed_status_regular_v6.png create mode 100644 tests/test_p3k.rs diff --git a/.gitignore b/.gitignore index 22a146e8..af76eba6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ target -dist \ No newline at end of file +dist +**/tests/snapshots/**/*.diff.png +**/tests/snapshots/**/*.new.png +.firebase diff --git a/Cargo.lock b/Cargo.lock index fe69d278..aba5104f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,102 @@ dependencies = [ "serde", ] +[[package]] +name = "accesskit_atspi_common" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c5dd55e6e94949498698daf4d48fb5659e824d7abec0d394089656ceaf99d4f" +dependencies = [ + "accesskit", + "accesskit_consumer 0.26.0", + "atspi-common", + "serde", + "thiserror 1.0.69", + "zvariant 4.2.0", +] + +[[package]] +name = "accesskit_consumer" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa3a17950ce0d911f132387777b9b3d05eddafb59b773ccaa53fceefaeb0228e" +dependencies = [ + "accesskit", + "immutable-chunkmap", +] + +[[package]] +name = "accesskit_consumer" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47983a1084940ba9a39c077a8c63e55c619388be5476ac04c804cfbd1e63459" +dependencies = [ + "accesskit", + "hashbrown", + "immutable-chunkmap", +] + +[[package]] +name = "accesskit_macos" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7329821f3bd1101e03a7d2e03bd339e3ac0dc64c70b4c9f9ae1949e3ba8dece1" +dependencies = [ + "accesskit", + "accesskit_consumer 0.26.0", + "hashbrown", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "accesskit_unix" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcee751cc20d88678c33edaf9c07e8b693cd02819fe89053776f5313492273f5" +dependencies = [ + "accesskit", + "accesskit_atspi_common", + "async-channel", + "async-executor", + "async-task", + "atspi", + "futures-lite", + "futures-util", + "serde", + "zbus 4.4.0", +] + +[[package]] +name = "accesskit_windows" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24fcd5d23d70670992b823e735e859374d694a3d12bfd8dd32bd3bd8bedb5d81" +dependencies = [ + "accesskit", + "accesskit_consumer 0.26.0", + "hashbrown", + "paste", + "static_assertions", + "windows 0.58.0", + "windows-core 0.58.0", +] + +[[package]] +name = "accesskit_winit" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6a48dad5530b6deb9fc7a52cc6c3bf72cdd9eb8157ac9d32d69f2427a5e879" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "raw-window-handle", + "winit", +] + [[package]] name = "addr2line" version = "0.24.2" @@ -245,7 +341,7 @@ dependencies = [ "serde", "serde_repr", "url", - "zbus", + "zbus 5.4.0", ] [[package]] @@ -408,6 +504,57 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atspi" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be534b16650e35237bb1ed189ba2aab86ce65e88cc84c66f4935ba38575cecbf" +dependencies = [ + "atspi-common", + "atspi-connection", + "atspi-proxies", +] + +[[package]] +name = "atspi-common" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1909ed2dc01d0a17505d89311d192518507e8a056a48148e3598fef5e7bb6ba7" +dependencies = [ + "enumflags2", + "serde", + "static_assertions", + "zbus 4.4.0", + "zbus-lockstep", + "zbus-lockstep-macros", + "zbus_names 3.0.0", + "zvariant 4.2.0", +] + +[[package]] +name = "atspi-connection" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "430c5960624a4baaa511c9c0fcc2218e3b58f5dbcc47e6190cafee344b873333" +dependencies = [ + "atspi-common", + "atspi-proxies", + "futures-lite", + "zbus 4.4.0", +] + +[[package]] +name = "atspi-proxies" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e6c5de3e524cf967569722446bcd458d5032348554d9a17d7d72b041ab7496" +dependencies = [ + "atspi-common", + "serde", + "zbus 4.4.0", + "zvariant 4.2.0", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -795,6 +942,16 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "combine" version = "4.6.7" @@ -1016,6 +1173,19 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "dify" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11217d469eafa3b809ad84651eb9797ccbb440b4a916d5d85cb1b994e89787f6" +dependencies = [ + "anyhow", + "colored", + "getopts", + "image", + "rayon", +] + [[package]] name = "digest" version = "0.10.7" @@ -1128,6 +1298,7 @@ dependencies = [ "objc2-foundation 0.2.2", "parking_lot", "percent-encoding", + "pollster", "profiling", "raw-window-handle", "ron", @@ -1137,6 +1308,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "web-time", + "wgpu", "winapi", "windows-sys 0.59.0", "winit", @@ -1205,6 +1377,7 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d9dfbb78fe4eb9c3a39ad528b90ee5915c252e77bbab9d4ebc576541ab67e13" dependencies = [ + "accesskit_winit", "ahash", "arboard", "bytemuck", @@ -1237,6 +1410,22 @@ dependencies = [ "winit", ] +[[package]] +name = "egui_kittest" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c46def610cf9486675aeec698d4e36a949ec0b0e1f6096135b0584dcfd52aa47" +dependencies = [ + "dify", + "eframe", + "egui", + "egui-wgpu", + "image", + "kittest", + "pollster", + "wgpu", +] + [[package]] name = "egui_plot" version = "0.31.0" @@ -1583,6 +1772,7 @@ dependencies = [ "futures-core", "futures-io", "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -1629,6 +1819,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -1782,6 +1981,18 @@ dependencies = [ "bitflags 2.8.0", ] +[[package]] +name = "gpu-allocator" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +dependencies = [ + "log", + "presser", + "thiserror 1.0.69", + "windows 0.57.0", +] + [[package]] name = "gpu-descriptor" version = "0.3.1" @@ -2240,6 +2451,15 @@ dependencies = [ "tiff", ] +[[package]] +name = "immutable-chunkmap" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f97096f508d54f8f8ab8957862eee2ccd628847b6217af1a335e1c44dee578" +dependencies = [ + "arrayvec", +] + [[package]] name = "indexmap" version = "2.7.1" @@ -2374,6 +2594,17 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kittest" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f659954571a3c132356bd15c25f0dcf14d270a28ec5c58797adc2f432831bed5" +dependencies = [ + "accesskit", + "accesskit_consumer 0.25.0", + "parking_lot", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -3259,6 +3490,7 @@ dependencies = [ "egui", "egui-notify", "egui-phosphor", + "egui_kittest", "egui_plot", "elevated-command", "env_logger", @@ -3374,6 +3606,12 @@ dependencies = [ "zerocopy 0.7.35", ] +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + [[package]] name = "pretty_assertions" version = "1.4.1" @@ -3474,6 +3712,16 @@ dependencies = [ "puffin", ] +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quick-xml" version = "0.37.2" @@ -3604,6 +3852,12 @@ dependencies = [ "getrandom 0.3.1", ] +[[package]] +name = "range-alloc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -5004,7 +5258,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.37.2", "quote", ] @@ -5085,6 +5339,7 @@ dependencies = [ "document-features", "js-sys", "log", + "naga", "parking_lot", "profiling", "raw-window-handle", @@ -5132,13 +5387,16 @@ dependencies = [ "android_system_properties", "arrayvec", "ash", + "bit-set", "bitflags 2.8.0", + "block", "bytemuck", "cfg_aliases", "core-graphics-types", "glow", "glutin_wgl_sys", "gpu-alloc", + "gpu-allocator", "gpu-descriptor", "js-sys", "khronos-egl", @@ -5153,6 +5411,7 @@ dependencies = [ "ordered-float", "parking_lot", "profiling", + "range-alloc", "raw-window-handle", "renderdoc-sys", "rustc-hash 1.1.0", @@ -5162,6 +5421,7 @@ dependencies = [ "web-sys", "wgpu-types", "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] @@ -5866,6 +6126,44 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros 4.4.0", + "zbus_names 3.0.0", + "zvariant 4.2.0", +] + [[package]] name = "zbus" version = "5.4.0" @@ -5897,9 +6195,46 @@ dependencies = [ "windows-sys 0.59.0", "winnow", "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 5.4.0", + "zbus_names 4.2.0", + "zvariant 5.3.0", +] + +[[package]] +name = "zbus-lockstep" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca2c5dceb099bddaade154055c926bb8ae507a18756ba1d8963fd7b51d8ed1d" +dependencies = [ + "zbus_xml", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus-lockstep-macros" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "zbus-lockstep", + "zbus_xml", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils 2.1.0", ] [[package]] @@ -5912,9 +6247,20 @@ dependencies = [ "proc-macro2", "quote", "syn", - "zbus_names", - "zvariant", - "zvariant_utils", + "zbus_names 4.2.0", + "zvariant 5.3.0", + "zvariant_utils 3.2.0", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant 4.2.0", ] [[package]] @@ -5926,7 +6272,20 @@ dependencies = [ "serde", "static_assertions", "winnow", - "zvariant", + "zvariant 5.3.0", +] + +[[package]] +name = "zbus_xml" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f374552b954f6abb4bd6ce979e6c9b38fb9d0cd7cc68a7d796e70c9f3a233" +dependencies = [ + "quick-xml 0.30.0", + "serde", + "static_assertions", + "zbus_names 3.0.0", + "zvariant 4.2.0", ] [[package]] @@ -6103,6 +6462,19 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive 4.2.0", +] + [[package]] name = "zvariant" version = "5.3.0" @@ -6115,8 +6487,21 @@ dependencies = [ "static_assertions", "url", "winnow", - "zvariant_derive", - "zvariant_utils", + "zvariant_derive 5.3.0", + "zvariant_utils 3.2.0", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils 2.1.0", ] [[package]] @@ -6129,7 +6514,18 @@ dependencies = [ "proc-macro2", "quote", "syn", - "zvariant_utils", + "zvariant_utils 3.2.0", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 539c0689..a93e4d1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,6 +99,9 @@ tokio = "1.44.2" testresult.workspace = true tempfile.workspace = true reqwest.workspace = true +# For GUI and GUI snapshot tests +egui_kittest = { version = "0.31.1", features = ["eframe", "snapshot", "wgpu", "x11"] } + # native: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/README.md b/README.md index dcc181ca..0bc503e2 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,10 @@ Inspect logs from SkyTEM systems. +## Example (snapshot) + +![plotinator3000_snapshot](./tests/snapshots/dropped_mbed_pid_regular_v6.png) + ## Why is this repository public? For inspiration/educational purposes. Anyone developing `egui`/`eframe` apps may or may not find any of the solutions in this repository useful for their own project(s). diff --git a/tests/snapshots/default_app_window.png b/tests/snapshots/default_app_window.png new file mode 100644 index 0000000000000000000000000000000000000000..46fee748ea97bacfcab4162220729e0b48a20eca GIT binary patch literal 122904 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 z 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%0D{;Legl$r$@d9vlrPJbYC;HHR#%FZW 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�n_G&5jwkB>K2gIf@PD`e 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 U!nMtZtr&p7)78&qol`;+0N7JwVgLXD literal 0 HcmV?d00001 diff --git a/tests/snapshots/default_mqtt_config_window.png b/tests/snapshots/default_mqtt_config_window.png new file mode 100644 index 0000000000000000000000000000000000000000..2e476643992e5bef3f2dd81cc9588199b4e3f9fc GIT binary patch literal 55149 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 literal 0 HcmV?d00001 diff --git a/tests/snapshots/dropped_mbed_pid_regular_v6.png b/tests/snapshots/dropped_mbed_pid_regular_v6.png new file mode 100644 index 0000000000000000000000000000000000000000..c99587e05062511d2a932805587c2e2a5b568f5e GIT binary patch literal 140652 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 z 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

gR463^#Dv_q+R~DlEQG}x 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

&Kg!u?jf8x-Ni2s}ir{a<07}IEri5x3 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 literal 0 HcmV?d00001 diff --git a/tests/snapshots/dropped_mbed_status_regular_v6.png b/tests/snapshots/dropped_mbed_status_regular_v6.png new file mode 100644 index 0000000000000000000000000000000000000000..547a5301bdcc09765b693471620e96dd5e205583 GIT binary patch literal 97647 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 Vr?u~^@P99kef5R!|1o35{{c<00>1zN literal 0 HcmV?d00001 diff --git a/tests/test_p3k.rs b/tests/test_p3k.rs new file mode 100644 index 00000000..32562baf --- /dev/null +++ b/tests/test_p3k.rs @@ -0,0 +1,139 @@ +use std::path::PathBuf; + +use egui::{DroppedFile, Event, Pos2, Rect, accesskit::Role}; +use egui_kittest::{ + Harness, + kittest::{Node, Queryable}, +}; +use test_util::{mbed_pid_v6_regular, mbed_status_v6_regular}; + +fn get_harness() -> Harness<'static, plotinator3000::App> { + Harness::new_eframe(|cc| plotinator3000::App::new(cc)) +} + +pub struct HarnessWrapper { + name: String, + harness: Harness<'static, plotinator3000::App>, +} + +impl HarnessWrapper { + pub fn new(name: impl Into) -> Self { + Self { + name: name.into(), + harness: get_harness(), + } + } + + pub fn inner(&mut self) -> &mut Harness<'static, plotinator3000::App> { + &mut self.harness + } + + pub fn run(&mut self) -> u64 { + self.harness.run() + } + + pub fn step(&mut self) { + self.harness.step(); + } + + /// Save a named snapshot, ensure that contents are fitted before taking the snapshot + pub fn save_snapshot(&mut self) { + self.harness.fit_contents(); + self.harness.snapshot(&self.name); + } + + pub fn drop_file(&mut self, path: PathBuf) { + let dropped_file = DroppedFile { + path: Some(path), + name: "".into(), + mime: "".into(), + last_modified: None, + bytes: None, + }; + self.harness.input_mut().dropped_files.push(dropped_file); + } + + pub fn input_event(&mut self, e: Event) { + self.harness.input_mut().events.push(e); + } + + pub fn get_screen_rect(&self) -> Rect { + self.harness.ctx.screen_rect() + } + + /* + Convenience getters for Plotinator3000 UI elements + */ + + pub fn get_homepage_node(&self) -> Node<'_> { + self.harness.get_by_role_and_label(Role::Label, "Homepage") + } + + pub fn get_mqtt_connect_button(&self) -> Node<'_> { + self.harness + .get_by_role_and_label(Role::Button, "MQTT connect") + } + + pub fn get_mqtt_configuration_window(&self) -> Node<'_> { + self.harness + .get_by_role_and_label(Role::Window, "MQTT Configuration") + } +} + +#[test] +fn test_snapshot_open_app() { + let mut harness = HarnessWrapper::new("default_app_window"); + harness.run(); + let homepage = harness.get_homepage_node(); + let main_window = homepage.parent().unwrap(); + assert_eq!(main_window.role(), Role::Window); + assert!(main_window.is_focused()); + + harness.save_snapshot(); +} + +#[test] +fn test_snapshot_open_mqtt_config_window_connect_disabled() { + let mut harness = HarnessWrapper::new("default_mqtt_config_window"); + let mqtt_button = harness.get_mqtt_connect_button(); + assert!(mqtt_button.is_clickable()); + + mqtt_button.click(); + harness.run(); + + let mqtt_cfg_window = harness.get_mqtt_configuration_window(); + + let _broker_addr_txt_input = mqtt_cfg_window + .get_by(|n| n.role() == Role::TextInput && n.value().is_some_and(|v| v == "127.0.0.1")); + + let connect_button = mqtt_cfg_window.get_by_role_and_label(Role::Button, "Connect"); + assert!(connect_button.is_disabled()); + + harness.save_snapshot(); +} + +#[test] +fn test_snapshot_drop_load_mbed_status_regular_v6() { + let mut harness = HarnessWrapper::new("dropped_mbed_status_regular_v6"); + harness.drop_file(mbed_status_v6_regular()); + harness.run(); + harness.save_snapshot(); +} + +#[test] +fn test_snapshot_drop_load_mbed_status_pid_v6_with_cursor_on_plot_window() { + let mut harness = HarnessWrapper::new("dropped_mbed_pid_regular_v6"); + harness.drop_file(mbed_pid_v6_regular()); + harness.run(); + + // Place the cursor in the middle plot area to see that the cursor "alighment-lines" are present + // across the plot areas + let center_pos = harness.get_screen_rect().center(); + let left_center_pos = harness.get_screen_rect().left_center(); + let offset_right = left_center_pos.x + center_pos.x / 2.; + let cursor_pos = Pos2::new(left_center_pos.x + offset_right, left_center_pos.y); + harness.input_event(egui::Event::PointerMoved(cursor_pos)); + harness.step(); + + harness.save_snapshot(); +} From eb8f085331dd0dd0f1a6a14cf5404190a9a00ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 22:04:51 +0200 Subject: [PATCH 34/48] fix lints --- tests/{test_p3k.rs => test_snapshot_plotinator3000.rs} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename tests/{test_p3k.rs => test_snapshot_plotinator3000.rs} (97%) diff --git a/tests/test_p3k.rs b/tests/test_snapshot_plotinator3000.rs similarity index 97% rename from tests/test_p3k.rs rename to tests/test_snapshot_plotinator3000.rs index 32562baf..1275b143 100644 --- a/tests/test_p3k.rs +++ b/tests/test_snapshot_plotinator3000.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use egui::{DroppedFile, Event, Pos2, Rect, accesskit::Role}; use egui_kittest::{ Harness, - kittest::{Node, Queryable}, + kittest::{Node, Queryable as _}, }; use test_util::{mbed_pid_v6_regular, mbed_status_v6_regular}; @@ -45,8 +45,8 @@ impl HarnessWrapper { pub fn drop_file(&mut self, path: PathBuf) { let dropped_file = DroppedFile { path: Some(path), - name: "".into(), - mime: "".into(), + name: String::new(), + mime: String::new(), last_modified: None, bytes: None, }; From d0fc7b87da54305b03153b98d9e3998196689f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 22:54:51 +0200 Subject: [PATCH 35/48] filter snapshot tests except on macos --- .github/workflows/CI.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2ae5763f..3bc0de72 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -78,7 +78,21 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: taiki-e/install-action@nextest - uses: Swatinem/rust-cache@v2 - - run: cargo nextest run --workspace --profile ci --run-ignored=all + + - name: Test ${{ matrix.os }} + shell: bash + run: | + if [ "${{ matrix.os }}" != "macos-latest" ]; then + FILTER="--filterset 'not test(test_snapshot)'" + fi + cargo nextest run --workspace --profile ci --run-ignored=all ${FILTER} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + if: matrix.os == 'macos-latest' && always() + with: + name: test-results + path: "**/tests/snapshots" format: runs-on: ubuntu-latest From 59bd0e63a3c6fc87aff601161bd19b6457f19d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 23:06:37 +0200 Subject: [PATCH 36/48] tweak --- .github/workflows/CI.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3bc0de72..af7033b8 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -80,12 +80,13 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Test ${{ matrix.os }} - shell: bash - run: | - if [ "${{ matrix.os }}" != "macos-latest" ]; then - FILTER="--filterset 'not test(test_snapshot)'" - fi - cargo nextest run --workspace --profile ci --run-ignored=all ${FILTER} + if: ${{ matrix.os }} == macos-latest + run: cargo nextest run --workspace --profile ci --run-ignored=all + + - name: Test ${{ matrix.os }} + if: ${{ matrix.os }} != macos-latest + run: + cargo nextest run --workspace --profile ci --run-ignored=all --filterset 'not test(test_snapshot)' - name: Upload artifacts uses: actions/upload-artifact@v4 From 420dd57966497741a37a87b5f04e4d889ddb9a6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 23:09:09 +0200 Subject: [PATCH 37/48] add confirm prompt to ci recipe --- just/ci.just | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/just/ci.just b/just/ci.just index 9d13683a..29157565 100644 --- a/just/ci.just +++ b/just/ci.just @@ -4,6 +4,7 @@ export RUSTFLAGS := "-D warnings" export RUSTDOCFLAGS := "-D warnings" export RUST_LOG := "debug" + ci: lint check fmt test trunk audit dist-plan # We only check the most common native linux target because that is the target we usually develop on, @@ -24,6 +25,11 @@ lint: typos # Run all tests with the CI profile and include all ignored tests +[confirm("\ +Are you sure you want to run tests in CI mode? +Some tests will touch your environment (e.g. .profile) +with installers and artifacts from the upgrade functionality +")] test: cargo nextest run --workspace --profile ci --run-ignored=all From 850072699f70c7bca4c066d040bacfe0fbdcd9fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 23:17:47 +0200 Subject: [PATCH 38/48] fix --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index af7033b8..0d9dc118 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -80,11 +80,11 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Test ${{ matrix.os }} - if: ${{ matrix.os }} == macos-latest + if: matrix.os == 'macos-latest' run: cargo nextest run --workspace --profile ci --run-ignored=all - name: Test ${{ matrix.os }} - if: ${{ matrix.os }} != macos-latest + if: matrix.os != 'macos-latest' run: cargo nextest run --workspace --profile ci --run-ignored=all --filterset 'not test(test_snapshot)' From 3ba88bb0df03a7f92d3eda3042bdb11e5be810aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Sun, 11 May 2025 23:18:41 +0200 Subject: [PATCH 39/48] tweak --- just/ci.just | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/just/ci.just b/just/ci.just index 29157565..c5173d8b 100644 --- a/just/ci.just +++ b/just/ci.just @@ -25,7 +25,7 @@ lint: typos # Run all tests with the CI profile and include all ignored tests -[confirm("\ +[confirm(" Are you sure you want to run tests in CI mode? Some tests will touch your environment (e.g. .profile) with installers and artifacts from the upgrade functionality From 1f14594dc370d7417b8eb55c6c837bc2d9ed9054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Mon, 12 May 2025 16:16:40 +0200 Subject: [PATCH 40/48] put snapshot test util into separate module --- tests/test_snapshot_plotinator3000.rs | 82 +-------------------------- tests/util/mod.rs | 75 ++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 80 deletions(-) create mode 100644 tests/util/mod.rs diff --git a/tests/test_snapshot_plotinator3000.rs b/tests/test_snapshot_plotinator3000.rs index 1275b143..78baa8ac 100644 --- a/tests/test_snapshot_plotinator3000.rs +++ b/tests/test_snapshot_plotinator3000.rs @@ -1,84 +1,6 @@ -use std::path::PathBuf; - -use egui::{DroppedFile, Event, Pos2, Rect, accesskit::Role}; -use egui_kittest::{ - Harness, - kittest::{Node, Queryable as _}, -}; +mod util; use test_util::{mbed_pid_v6_regular, mbed_status_v6_regular}; - -fn get_harness() -> Harness<'static, plotinator3000::App> { - Harness::new_eframe(|cc| plotinator3000::App::new(cc)) -} - -pub struct HarnessWrapper { - name: String, - harness: Harness<'static, plotinator3000::App>, -} - -impl HarnessWrapper { - pub fn new(name: impl Into) -> Self { - Self { - name: name.into(), - harness: get_harness(), - } - } - - pub fn inner(&mut self) -> &mut Harness<'static, plotinator3000::App> { - &mut self.harness - } - - pub fn run(&mut self) -> u64 { - self.harness.run() - } - - pub fn step(&mut self) { - self.harness.step(); - } - - /// Save a named snapshot, ensure that contents are fitted before taking the snapshot - pub fn save_snapshot(&mut self) { - self.harness.fit_contents(); - self.harness.snapshot(&self.name); - } - - pub fn drop_file(&mut self, path: PathBuf) { - let dropped_file = DroppedFile { - path: Some(path), - name: String::new(), - mime: String::new(), - last_modified: None, - bytes: None, - }; - self.harness.input_mut().dropped_files.push(dropped_file); - } - - pub fn input_event(&mut self, e: Event) { - self.harness.input_mut().events.push(e); - } - - pub fn get_screen_rect(&self) -> Rect { - self.harness.ctx.screen_rect() - } - - /* - Convenience getters for Plotinator3000 UI elements - */ - - pub fn get_homepage_node(&self) -> Node<'_> { - self.harness.get_by_role_and_label(Role::Label, "Homepage") - } - - pub fn get_mqtt_connect_button(&self) -> Node<'_> { - self.harness - .get_by_role_and_label(Role::Button, "MQTT connect") - } - - pub fn get_mqtt_configuration_window(&self) -> Node<'_> { - self.harness - .get_by_role_and_label(Role::Window, "MQTT Configuration") - } -} +use util::*; #[test] fn test_snapshot_open_app() { diff --git a/tests/util/mod.rs b/tests/util/mod.rs new file mode 100644 index 00000000..39aa5409 --- /dev/null +++ b/tests/util/mod.rs @@ -0,0 +1,75 @@ +pub use egui::{DroppedFile, Event, Pos2, Rect, accesskit::Role}; +pub use egui_kittest::{ + Harness, + kittest::{Node, Queryable as _}, +}; +pub use std::path::PathBuf; + +pub fn get_harness() -> Harness<'static, plotinator3000::App> { + Harness::new_eframe(|cc| plotinator3000::App::new(cc)) +} + +pub struct HarnessWrapper { + name: String, + harness: Harness<'static, plotinator3000::App>, +} + +impl HarnessWrapper { + pub fn new(name: impl Into) -> Self { + Self { + name: name.into(), + harness: get_harness(), + } + } + + pub fn run(&mut self) -> u64 { + self.harness.run() + } + + pub fn step(&mut self) { + self.harness.step(); + } + + /// Save a named snapshot, ensure that contents are fitted before taking the snapshot + pub fn save_snapshot(&mut self) { + self.harness.fit_contents(); + self.harness.snapshot(&self.name); + } + + pub fn drop_file(&mut self, path: PathBuf) { + let dropped_file = DroppedFile { + path: Some(path), + name: String::new(), + mime: String::new(), + last_modified: None, + bytes: None, + }; + self.harness.input_mut().dropped_files.push(dropped_file); + } + + pub fn input_event(&mut self, e: Event) { + self.harness.input_mut().events.push(e); + } + + pub fn get_screen_rect(&self) -> Rect { + self.harness.ctx.screen_rect() + } + + /* + Convenience getters for Plotinator3000 UI elements + */ + + pub fn get_homepage_node(&self) -> Node<'_> { + self.harness.get_by_role_and_label(Role::Label, "Homepage") + } + + pub fn get_mqtt_connect_button(&self) -> Node<'_> { + self.harness + .get_by_role_and_label(Role::Button, "MQTT connect") + } + + pub fn get_mqtt_configuration_window(&self) -> Node<'_> { + self.harness + .get_by_role_and_label(Role::Window, "MQTT Configuration") + } +} From fa609504fe2f740778b97acd0ef33650658665b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Mon, 12 May 2025 16:19:11 +0200 Subject: [PATCH 41/48] allow lint --- tests/util/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 39aa5409..1adb28b3 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,3 +1,7 @@ +#![allow( + clippy::disallowed_types, + reason = "This is test utilities so things like PathBuf is fine, we won't deploy this code anywhere" +)] pub use egui::{DroppedFile, Event, Pos2, Rect, accesskit::Role}; pub use egui_kittest::{ Harness, From 49ccf3d4be4b453a6de01b590ab428d0ea12cb55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Mon, 12 May 2025 16:49:27 +0200 Subject: [PATCH 42/48] changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92c64022..0b82f0ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Switch to `mimalloc` as the global allocator for significant performance improvements (~20%) +### Internal + +- Better encapsulation of MQTT features in GUI code +- Added GUI tests, including snapshot tests + ## [1.12.0] ### Changed From f4ffe866821c656cf102093baff950cff76959f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Mon, 12 May 2025 16:50:41 +0200 Subject: [PATCH 43/48] cargo update --- CHANGELOG.md | 4 + Cargo.lock | 1044 +++++++++++++++++++++++++++----------------------- 2 files changed, 561 insertions(+), 487 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b82f0ec..95af2ec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Switch to `mimalloc` as the global allocator for significant performance improvements (~20%) +### Dependencies + +- Run `cargo update` + ### Internal - Better encapsulation of MQTT features in GUI code diff --git a/Cargo.lock b/Cargo.lock index 26b1281f..cde44a75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,16 +152,16 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.2.15", + "getrandom 0.3.3", "once_cell", "serde", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -180,7 +180,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.8.0", + "bitflags 2.9.0", "cc", "cesu8", "jni", @@ -282,19 +282,21 @@ dependencies = [ [[package]] name = "arboard" -version = "3.4.1" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" +checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70" dependencies = [ "clipboard-win", - "core-graphics", "image", "log", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", + "objc2 0.6.1", + "objc2-app-kit 0.3.1", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.1", "parking_lot", - "windows-sys 0.48.0", + "percent-encoding", + "windows-sys 0.59.0", "x11rb", ] @@ -336,12 +338,12 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.0", + "rand 0.9.1", "raw-window-handle", "serde", "serde_repr", "url", - "zbus 5.4.0", + "zbus 5.6.0", ] [[package]] @@ -370,14 +372,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", + "pin-project-lite", "slab", ] @@ -489,9 +492,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.86" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", @@ -637,9 +640,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -694,9 +697,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" dependencies = [ "serde", ] @@ -727,11 +730,11 @@ dependencies = [ [[package]] name = "block2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" dependencies = [ - "objc2 0.6.0", + "objc2 0.6.1", ] [[package]] @@ -755,18 +758,18 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.21.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.8.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" +checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" dependencies = [ "proc-macro2", "quote", @@ -787,9 +790,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bzip2" @@ -816,7 +819,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "log", "polling", "rustix 0.38.44", @@ -847,9 +850,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.12" +version = "1.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" +checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" dependencies = [ "jobserver", "libc", @@ -885,9 +888,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -919,9 +922,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.53" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24a03c8b52922d68a1589ad61032f2c1aa5a8158d2aa0d93c6e9534944bbad6" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" dependencies = [ "cc", ] @@ -949,7 +952,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -1047,9 +1050,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -1127,9 +1130,9 @@ checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -1209,10 +1212,20 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" dependencies = [ - "bitflags 2.8.0", - "block2 0.6.0", + "bitflags 2.9.0", + "block2 0.6.1", "libc", - "objc2 0.6.0", + "objc2 0.6.1", +] + +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.1", ] [[package]] @@ -1237,9 +1250,9 @@ dependencies = [ [[package]] name = "document-features" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" dependencies = [ "litrs", ] @@ -1252,15 +1265,15 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "dyn-clone" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "ecolor" @@ -1323,7 +1336,7 @@ dependencies = [ "accesskit", "ahash", "backtrace", - "bitflags 2.8.0", + "bitflags 2.9.0", "emath", "epaint", "log", @@ -1440,9 +1453,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elevated-command" @@ -1566,15 +1579,15 @@ checksum = "fc7e7a64c02cf7a5b51e745a9e45f60660a286f151c238b9d397b3e923f5082f" [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -1582,9 +1595,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.3.1" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" [[package]] name = "event-listener" @@ -1599,9 +1612,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ "event-listener", "pin-project-lite", @@ -1624,9 +1637,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -1651,9 +1664,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "foreign-types" @@ -1830,9 +1843,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", @@ -1843,16 +1856,16 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.13.3+wasi-0.2.2", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", "wasm-bindgen", - "windows-targets 0.52.6", ] [[package]] @@ -1898,22 +1911,22 @@ dependencies = [ [[package]] name = "glutin" -version = "0.32.2" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03642b8b0cce622392deb0ee3e88511f75df2daac806102597905c3ea1974848" +checksum = "12124de845cacfebedff80e877bb37b5b75c34c5a4c89e47e1cdd67fb6041325" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg_aliases", "cgl", - "core-foundation 0.9.4", - "dispatch", + "dispatch2 0.3.0", "glutin_egl_sys", "glutin_glx_sys", "glutin_wgl_sys", "libloading", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", + "objc2 0.6.1", + "objc2-app-kit 0.3.1", + "objc2-core-foundation", + "objc2-foundation 0.3.1", "once_cell", "raw-window-handle", "wayland-sys", @@ -1968,7 +1981,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "gpu-alloc-types", ] @@ -1978,7 +1991,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -1990,7 +2003,7 @@ dependencies = [ "log", "presser", "thiserror 1.0.69", - "windows 0.57.0", + "windows 0.58.0", ] [[package]] @@ -1999,7 +2012,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "gpu-descriptor-types", "hashbrown", ] @@ -2010,14 +2023,14 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] name = "h2" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", @@ -2034,9 +2047,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "foldhash", ] @@ -2047,7 +2060,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f8592329337a96c802cccdbd11fe5f9476cb940d5a88dcca729a2ecf82e1c9" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "hdf5-metno-derive", "hdf5-metno-sys", @@ -2099,9 +2112,9 @@ dependencies = [ [[package]] name = "hdf5-metno-types" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69effedfce92d28166b034585cdd62547a6aa92d1cd7a23d3215bb40fab91960" +checksum = "86784ab948de3b7ffdf4c64561fdc1ec86ec512054e04ca649eda65ad0c6faa9" dependencies = [ "ascii", "cfg-if", @@ -2165,9 +2178,9 @@ dependencies = [ [[package]] name = "http" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -2186,12 +2199,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", + "futures-core", "http", "http-body", "pin-project-lite", @@ -2199,9 +2212,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" @@ -2233,12 +2246,12 @@ dependencies = [ "http", "hyper", "hyper-util", - "rustls 0.23.22", + "rustls 0.23.27", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tower-service", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -2259,9 +2272,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", @@ -2269,6 +2282,7 @@ dependencies = [ "http", "http-body", "hyper", + "libc", "pin-project-lite", "socket2", "tokio", @@ -2278,16 +2292,17 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core 0.61.0", ] [[package]] @@ -2301,21 +2316,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -2324,31 +2340,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -2356,67 +2352,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "idna" version = "1.0.3" @@ -2430,9 +2413,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -2440,9 +2423,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.5" +version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" dependencies = [ "bytemuck", "byteorder-lite", @@ -2462,9 +2445,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown", @@ -2472,9 +2455,9 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] @@ -2502,15 +2485,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.4" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" dependencies = [ "jiff-static", "log", @@ -2521,9 +2504,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.4" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" dependencies = [ "proc-macro2", "quote", @@ -2554,10 +2537,11 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.3", "libc", ] @@ -2613,18 +2597,18 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] @@ -2643,9 +2627,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.12", ] [[package]] @@ -2656,15 +2640,15 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "litrs" @@ -2682,12 +2666,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "lockfree-object-pool" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" - [[package]] name = "log" version = "0.4.27" @@ -2703,6 +2681,12 @@ dependencies = [ "serde", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "lz4_flex" version = "0.11.3" @@ -2779,7 +2763,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block", "core-graphics-types", "foreign-types 0.5.0", @@ -2790,21 +2774,20 @@ dependencies = [ [[package]] name = "miette" -version = "7.5.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a955165f87b37fd1862df2a59547ac542c77ef6d17c666f619d1ad22dd89484" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ "cfg-if", "miette-derive", - "thiserror 1.0.69", "unicode-width", ] [[package]] name = "miette-derive" -version = "7.5.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", @@ -2828,9 +2811,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -2855,7 +2838,7 @@ checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg_aliases", "codespan-reporting", "hexf-parse", @@ -2871,9 +2854,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -2907,7 +2890,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "jni-sys", "log", "ndk-sys 0.6.0+11769913", @@ -2946,7 +2929,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "cfg_aliases", "libc", @@ -3040,9 +3023,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" dependencies = [ "objc2-encode", ] @@ -3053,7 +3036,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -3065,14 +3048,16 @@ dependencies = [ [[package]] name = "objc2-app-kit" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "bitflags 2.8.0", - "block2 0.6.0", - "objc2 0.6.0", - "objc2-foundation 0.3.0", + "bitflags 2.9.0", + "block2 0.6.1", + "objc2 0.6.1", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.1", ] [[package]] @@ -3081,7 +3066,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -3105,7 +3090,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3113,12 +3098,26 @@ dependencies = [ [[package]] name = "objc2-core-foundation" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.0", + "dispatch2 0.3.0", + "objc2 0.6.1", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ - "bitflags 2.8.0", - "objc2 0.6.0", + "bitflags 2.9.0", + "dispatch2 0.3.0", + "objc2 0.6.1", + "objc2-core-foundation", + "objc2-io-surface", ] [[package]] @@ -3157,7 +3156,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2 0.5.1", "dispatch", "libc", @@ -3166,12 +3165,23 @@ dependencies = [ [[package]] name = "objc2-foundation" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.1", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" dependencies = [ - "bitflags 2.8.0", - "objc2 0.6.0", + "bitflags 2.9.0", + "objc2 0.6.1", "objc2-core-foundation", ] @@ -3193,7 +3203,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3205,7 +3215,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3228,7 +3238,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit", @@ -3260,7 +3270,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -3278,9 +3288,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl" @@ -3288,7 +3298,7 @@ version = "0.10.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -3316,9 +3326,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.107" +version = "0.9.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" dependencies = [ "cc", "libc", @@ -3387,7 +3397,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.12", "smallvec", "windows-targets 0.52.6", ] @@ -3416,18 +3426,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", @@ -3459,9 +3469,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plot_util" @@ -3578,9 +3588,9 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" [[package]] name = "portable-atomic" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] name = "portable-atomic-util" @@ -3591,6 +3601,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -3599,11 +3618,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -3624,9 +3643,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] @@ -3655,9 +3674,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -3724,43 +3743,46 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.37.2" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] [[package]] name = "quinn" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" dependencies = [ "bytes", + "cfg_aliases", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash 2.1.1", - "rustls 0.23.22", + "rustls 0.23.27", "socket2", "thiserror 2.0.12", "tokio", "tracing", + "web-time", ] [[package]] name = "quinn-proto" -version = "0.11.9" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" dependencies = [ "bytes", - "getrandom 0.2.15", - "rand 0.8.5", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.1", "ring", "rustc-hash 2.1.1", - "rustls 0.23.22", + "rustls 0.23.27", "rustls-pki-types", "slab", "thiserror 2.0.12", @@ -3771,9 +3793,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.9" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" dependencies = [ "cfg_aliases", "libc", @@ -3785,13 +3807,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.8.5" @@ -3805,13 +3833,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.23", ] [[package]] @@ -3840,7 +3867,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -3849,7 +3876,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.3", ] [[package]] @@ -3901,11 +3928,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -3971,7 +3998,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.22", + "rustls 0.23.27", "rustls-pemfile", "rustls-pki-types", "serde", @@ -3981,14 +4008,14 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tower", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.26.11", "windows-registry", ] @@ -3999,14 +4026,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" dependencies = [ "ashpd", - "block2 0.6.0", - "dispatch2", + "block2 0.6.1", + "dispatch2 0.2.0", "js-sys", "log", - "objc2 0.6.0", - "objc2-app-kit 0.3.0", + "objc2 0.6.1", + "objc2-app-kit 0.3.1", "objc2-core-foundation", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", "pollster", "raw-window-handle", "urlencoding", @@ -4018,13 +4045,13 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -4037,7 +4064,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64 0.21.7", - "bitflags 2.8.0", + "bitflags 2.9.0", "serde", "serde_derive", ] @@ -4054,7 +4081,7 @@ dependencies = [ "log", "rustls-native-certs", "rustls-pemfile", - "rustls-webpki", + "rustls-webpki 0.102.8", "thiserror 1.0.69", "tokio", "tokio-rustls 0.25.0", @@ -4084,7 +4111,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4093,14 +4120,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.1" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dade4812df5c384711475be5fcd8c162555352945401aed22a35bffeab61f657" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys 0.9.2", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] @@ -4113,21 +4140,21 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.102.8", "subtle", "zeroize", ] [[package]] name = "rustls" -version = "0.23.22" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.103.3", "subtle", "zeroize", ] @@ -4156,11 +4183,12 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ "web-time", + "zeroize", ] [[package]] @@ -4174,17 +4202,28 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.103.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -4206,9 +4245,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ "dyn-clone", "schemars_derive", @@ -4218,9 +4257,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" dependencies = [ "proc-macro2", "quote", @@ -4246,7 +4285,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -4334,9 +4373,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", @@ -4383,9 +4422,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -4459,9 +4498,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "smithay-client-toolkit" @@ -4469,7 +4508,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "calloop", "calloop-wayland-source", "cursor-icon", @@ -4510,9 +4549,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -4533,7 +4572,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -4597,9 +4636,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.98" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -4617,9 +4656,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -4632,7 +4671,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -4649,14 +4688,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.1", + "getrandom 0.3.3", "once_cell", - "rustix 1.0.1", + "rustix 1.0.7", "windows-sys 0.59.0", ] @@ -4736,9 +4775,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.37" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "num-conv", @@ -4749,15 +4788,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -4765,9 +4804,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -4780,9 +4819,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -4830,19 +4869,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.22", + "rustls 0.23.27", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -4853,9 +4892,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -4865,26 +4904,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.23" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + [[package]] name = "tower" version = "0.5.2" @@ -4967,9 +5013,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "uds_windows" @@ -4984,9 +5030,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-segmentation" @@ -5030,12 +5076,6 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -5087,9 +5127,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -5167,9 +5207,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" dependencies = [ "cc", "downcast-rs", @@ -5181,11 +5221,11 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "rustix 0.38.44", "wayland-backend", "wayland-scanner", @@ -5197,16 +5237,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" +checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" dependencies = [ "rustix 0.38.44", "wayland-client", @@ -5215,11 +5255,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.6" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" +checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -5227,11 +5267,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" +checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5240,11 +5280,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" +checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5258,7 +5298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" dependencies = [ "proc-macro2", - "quick-xml 0.37.2", + "quick-xml 0.37.5", "quote", ] @@ -5296,27 +5336,35 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea9fe1ebb156110ff855242c1101df158b822487e4957b0556d9ffce9db0f535" +checksum = "d5df295f8451142f1856b1bd86a606dfe9587d439bc036e319c827700dbd555e" dependencies = [ - "block2 0.5.1", "core-foundation 0.10.0", "home", "jni", "log", "ndk-context", - "objc2 0.5.2", - "objc2-foundation 0.2.2", + "objc2 0.6.1", + "objc2-foundation 0.3.1", "url", "web-sys", ] [[package]] name = "webpki-roots" -version = "0.26.8" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.0", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" dependencies = [ "rustls-pki-types", ] @@ -5329,12 +5377,12 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "wgpu" -version = "24.0.1" +version = "24.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47f55718f85c2fa756edffa0e7f0e0a60aba463d1362b57e23123c58f035e4b6" +checksum = "35904fb00ba2d2e0a4d002fcbbb6e1b89b574d272a50e5fc95f6e81cf281c245" dependencies = [ "arrayvec", - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg_aliases", "document-features", "js-sys", @@ -5355,13 +5403,13 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "24.0.0" +version = "24.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a39b8842dc9ffcbe34346e3ab6d496b32a47f6497e119d762c97fcaae3cb37" +checksum = "671c25545d479b47d3f0a8e373aceb2060b67c6eb841b24ac8c32348151c7a0c" dependencies = [ "arrayvec", "bit-vec", - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg_aliases", "document-features", "indexmap", @@ -5380,15 +5428,15 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "24.0.0" +version = "24.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a782e5056b060b0b4010881d1decddd059e44f2ecd01e2db2971b48ad3627e5" +checksum = "f112f464674ca69f3533248508ee30cb84c67cf06c25ff6800685f5e0294e259" dependencies = [ "android_system_properties", "arrayvec", "ash", "bit-set", - "bitflags 2.8.0", + "bitflags 2.9.0", "block", "bytemuck", "cfg_aliases", @@ -5430,7 +5478,7 @@ version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "js-sys", "log", "web-sys", @@ -5438,9 +5486,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" [[package]] name = "winapi" @@ -5537,6 +5585,19 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.2", + "windows-strings 0.4.0", +] + [[package]] name = "windows-implement" version = "0.57.0" @@ -5559,6 +5620,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-interface" version = "0.57.0" @@ -5581,11 +5653,22 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] name = "windows-registry" @@ -5593,7 +5676,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ - "windows-result 0.3.1", + "windows-result 0.3.2", "windows-strings 0.3.1", "windows-targets 0.53.0", ] @@ -5618,9 +5701,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ "windows-link", ] @@ -5644,6 +5727,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -5924,14 +6016,14 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winit" -version = "0.30.8" +version = "0.30.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d74280aabb958072864bff6cfbcf9025cf8bfacdde5e32b5e12920ef703b0f" +checksum = "b0d05bd8908e14618c9609471db04007e644fd9cce6529756046cfc577f9155e" dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.8.0", + "bitflags 2.9.0", "block2 0.5.1", "bytemuck", "calloop", @@ -5975,9 +6067,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.1" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -5995,24 +6087,18 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "x11-dl" @@ -6068,7 +6154,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "dlib", "log", "once_cell", @@ -6083,9 +6169,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" [[package]] name = "xz2" @@ -6104,9 +6190,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -6116,9 +6202,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -6166,13 +6252,12 @@ dependencies = [ [[package]] name = "zbus" -version = "5.4.0" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbddd8b6cb25d5d8ec1b23277b45299a98bfb220f1761ca11e186d5c702507f8" +checksum = "2522b82023923eecb0b366da727ec883ace092e7887b61d3da5139f26b44da58" dependencies = [ "async-broadcast", "async-executor", - "async-fs", "async-io", "async-lock", "async-process", @@ -6183,21 +6268,19 @@ dependencies = [ "enumflags2", "event-listener", "futures-core", - "futures-util", + "futures-lite", "hex", "nix", "ordered-stream", "serde", "serde_repr", - "static_assertions", "tracing", "uds_windows", "windows-sys 0.59.0", "winnow", - "xdg-home", - "zbus_macros 5.4.0", + "zbus_macros 5.6.0", "zbus_names 4.2.0", - "zvariant 5.3.0", + "zvariant 5.5.1", ] [[package]] @@ -6239,16 +6322,16 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.4.0" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac404d48b4e9cf193c8b49589f3280ceca5ff63519e7e64f55b4cf9c47ce146" +checksum = "05d2e12843c75108c00c618c2e8ef9675b50b6ec095b36dc965f2e5aed463c15" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn", "zbus_names 4.2.0", - "zvariant 5.3.0", + "zvariant 5.5.1", "zvariant_utils 3.2.0", ] @@ -6272,7 +6355,7 @@ dependencies = [ "serde", "static_assertions", "winnow", - "zvariant 5.3.0", + "zvariant 5.5.1", ] [[package]] @@ -6290,39 +6373,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" -dependencies = [ - "zerocopy-derive 0.8.23", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "proc-macro2", - "quote", - "syn", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.23" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", @@ -6331,18 +6393,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", @@ -6370,11 +6432,22 @@ dependencies = [ "syn", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -6383,9 +6456,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", @@ -6406,7 +6479,7 @@ dependencies = [ "crossbeam-utils", "deflate64", "flate2", - "getrandom 0.3.1", + "getrandom 0.3.3", "hmac", "indexmap", "lzma-rs", @@ -6422,41 +6495,39 @@ dependencies = [ [[package]] name = "zopfli" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" dependencies = [ "bumpalo", "crc32fast", - "lockfree-object-pool", "log", - "once_cell", "simd-adler32", ] [[package]] name = "zstd" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ "cc", "pkg-config", @@ -6477,17 +6548,16 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.3.0" +version = "5.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c951c21879c6e1d46ac5adfc34f698fefb465d498cf4ac87545849bd71bb5a" +checksum = "557e89d54880377a507c94cd5452f20e35d14325faf9d2958ebeadce0966c1b2" dependencies = [ "endi", "enumflags2", "serde", - "static_assertions", "url", "winnow", - "zvariant_derive 5.3.0", + "zvariant_derive 5.5.1", "zvariant_utils 3.2.0", ] @@ -6506,9 +6576,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.3.0" +version = "5.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eeb539471af098d9e63faf428c71ac4cd4efe0b5baa3c8a6b991c5f2543b70e" +checksum = "757779842a0d242061d24c28be589ce392e45350dfb9186dfd7a042a2e19870c" dependencies = [ "proc-macro-crate", "proc-macro2", From 6d92efd858005f3181eee9c68b765dc7599b61e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Wed, 14 May 2025 10:25:54 +0200 Subject: [PATCH 44/48] test with no snapshot --- tests/util/mod.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 1adb28b3..e2bb3049 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -36,8 +36,19 @@ impl HarnessWrapper { /// Save a named snapshot, ensure that contents are fitted before taking the snapshot pub fn save_snapshot(&mut self) { + let is_macos = cfg!(target_os = "macos"); self.harness.fit_contents(); - self.harness.snapshot(&self.name); + if std::env::var("CI").is_ok_and(|v| v == "true") { + // Only macos runners have access to a GPU + if is_macos { + self.harness.fit_contents(); + let opt = egui_kittest::SnapshotOptions::new().threshold(70.0); + self.harness.snapshot_options(&self.name, &opt); + } + } else { + self.harness.fit_contents(); + self.harness.snapshot(&self.name); + } } pub fn drop_file(&mut self, path: PathBuf) { From 811fde01aa83ea2c45c863cced32db4ffa991db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Wed, 14 May 2025 10:38:01 +0200 Subject: [PATCH 45/48] try always running gui tests --- .github/workflows/CI.yml | 2 +- tests/test_snapshot_plotinator3000.rs | 4 +++- tests/util/mod.rs | 31 ++++++++++++++++++++++++--- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0d9dc118..23bb0258 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -86,7 +86,7 @@ jobs: - name: Test ${{ matrix.os }} if: matrix.os != 'macos-latest' run: - cargo nextest run --workspace --profile ci --run-ignored=all --filterset 'not test(test_snapshot)' + cargo nextest run --workspace --profile ci --run-ignored=all - name: Upload artifacts uses: actions/upload-artifact@v4 diff --git a/tests/test_snapshot_plotinator3000.rs b/tests/test_snapshot_plotinator3000.rs index 78baa8ac..1447be10 100644 --- a/tests/test_snapshot_plotinator3000.rs +++ b/tests/test_snapshot_plotinator3000.rs @@ -57,5 +57,7 @@ fn test_snapshot_drop_load_mbed_status_pid_v6_with_cursor_on_plot_window() { harness.input_event(egui::Event::PointerMoved(cursor_pos)); harness.step(); - harness.save_snapshot(); + // We allow a larger diff threshold because this has a lot of narrow lines, which will give rise to + // a higher diff from GPU to GPU + harness.save_snapshot_with_threshold(CiThreshold(62.0)); } diff --git a/tests/util/mod.rs b/tests/util/mod.rs index e2bb3049..1393785c 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -13,6 +13,20 @@ pub fn get_harness() -> Harness<'static, plotinator3000::App> { Harness::new_eframe(|cc| plotinator3000::App::new(cc)) } +const DEFAULT_CI_DIFF_THRESHOLD: f32 = 1.0; + +/// specifies how much difference we allow in CI before a snapshot diff is an error. +/// +/// Default is `1.0` +#[derive(Clone, Copy)] +pub struct CiThreshold(pub f32); + +impl Default for CiThreshold { + fn default() -> Self { + Self(DEFAULT_CI_DIFF_THRESHOLD) + } +} + pub struct HarnessWrapper { name: String, harness: Harness<'static, plotinator3000::App>, @@ -36,17 +50,28 @@ impl HarnessWrapper { /// Save a named snapshot, ensure that contents are fitted before taking the snapshot pub fn save_snapshot(&mut self) { + self.save_snapshot_with_threshold(CiThreshold::default()); + } + + /// Save a named snapshot, ensure that contents are fitted before taking the snapshot + /// + /// `ci_threshold` specifies how much difference we allow in CI before a snapshot diff is an error. + /// + /// In CI the snapshot rendering is done on a Mac OS runner, as they are the only ones with + /// access to a GPU. Typically a threshold of 1-2 is enough to not get false positives, + /// but for a snapshot that includes a plot with lots of narrow lines (like plotting Mbed PID log) + /// the threshold will need to be higher. + pub fn save_snapshot_with_threshold(&mut self, threshold: CiThreshold) { let is_macos = cfg!(target_os = "macos"); self.harness.fit_contents(); + if std::env::var("CI").is_ok_and(|v| v == "true") { // Only macos runners have access to a GPU if is_macos { - self.harness.fit_contents(); - let opt = egui_kittest::SnapshotOptions::new().threshold(70.0); + let opt = egui_kittest::SnapshotOptions::new().threshold(threshold.0); self.harness.snapshot_options(&self.name, &opt); } } else { - self.harness.fit_contents(); self.harness.snapshot(&self.name); } } From 47c20fb3d02eb7e5bed132b2c68f3eb2f8523b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Wed, 14 May 2025 10:54:56 +0200 Subject: [PATCH 46/48] fix wrong doc reference --- .github/workflows/CI.yml | 5 ----- crates/log_if/src/plotable.rs | 2 +- tests/util/mod.rs | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 23bb0258..9d8d7494 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -83,11 +83,6 @@ jobs: if: matrix.os == 'macos-latest' run: cargo nextest run --workspace --profile ci --run-ignored=all - - name: Test ${{ matrix.os }} - if: matrix.os != 'macos-latest' - run: - cargo nextest run --workspace --profile ci --run-ignored=all - - name: Upload artifacts uses: actions/upload-artifact@v4 if: matrix.os == 'macos-latest' && always() diff --git a/crates/log_if/src/plotable.rs b/crates/log_if/src/plotable.rs index 3bd8db80..98158187 100644 --- a/crates/log_if/src/plotable.rs +++ b/crates/log_if/src/plotable.rs @@ -65,7 +65,7 @@ impl RawPlot { } } -/// [`PlotLabel`] represents some text label that should be displayed in the plot +/// [`PlotLabels`] represents some text label that should be displayed in the plot #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct PlotLabels { label_points: Vec<([f64; 2], String)>, diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 1393785c..008d63fe 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -55,7 +55,7 @@ impl HarnessWrapper { /// Save a named snapshot, ensure that contents are fitted before taking the snapshot /// - /// `ci_threshold` specifies how much difference we allow in CI before a snapshot diff is an error. + /// [`CiThreshold`] specifies how much difference we allow in CI before a snapshot diff is an error. /// /// In CI the snapshot rendering is done on a Mac OS runner, as they are the only ones with /// access to a GPU. Typically a threshold of 1-2 is enough to not get false positives, From 12de9060531979e3d7f08df06aa2a2e7b413898b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Wed, 14 May 2025 10:57:38 +0200 Subject: [PATCH 47/48] add some nice-to-have aliases --- .github/workflows/CI.yml | 3 ++- Justfile | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9d8d7494..1b859fe3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -83,7 +83,8 @@ jobs: if: matrix.os == 'macos-latest' run: cargo nextest run --workspace --profile ci --run-ignored=all - - name: Upload artifacts + # So that we can inspect why it failed + - name: Upload snapshots (including diffs) uses: actions/upload-artifact@v4 if: matrix.os == 'macos-latest' && always() with: diff --git a/Justfile b/Justfile index 63c1a1d8..b6314b69 100644 --- a/Justfile +++ b/Justfile @@ -10,6 +10,10 @@ PROJECT_NAME := "plotinator3000" alias t := test alias l := lint +alias lint-native := clippy-native +alias ln := clippy-native +alias lint-web := clippy-wasm +alias lw := clippy-wasm alias fmt := format alias f := format alias d := doc From 500a73c41777c958ac9666ce75837789bf988918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Beck=20K=C3=B6nig?= Date: Mon, 19 May 2025 11:23:32 +0200 Subject: [PATCH 48/48] add ValidatorStatus to make it more clear and readable what the state of broker validation is --- .../plotinator_mqtt/src/broker_validator.rs | 126 ++++++++++-------- crates/plotinator_mqtt/src/lib.rs | 2 +- crates/plotinator_mqtt/src/mqtt_cfg_window.rs | 9 +- src/mqtt/mqtt_window.rs | 23 ++-- 4 files changed, 93 insertions(+), 67 deletions(-) diff --git a/crates/plotinator_mqtt/src/broker_validator.rs b/crates/plotinator_mqtt/src/broker_validator.rs index 4b659b37..7a1f419a 100644 --- a/crates/plotinator_mqtt/src/broker_validator.rs +++ b/crates/plotinator_mqtt/src/broker_validator.rs @@ -1,10 +1,10 @@ +use crate::util::timestamped_client_id; +use rumqttc::{Client, Event, MqttOptions, Packet}; use std::{ net::{Ipv6Addr, SocketAddr, TcpStream, ToSocketAddrs as _}, - sync::mpsc, + sync::mpsc::{self, Sender}, time::{Duration, Instant}, }; -use rumqttc::{Client, Event, MqttOptions, Packet}; -use crate::util::timestamped_client_id; #[derive(Debug, Default, Clone, PartialEq, Eq)] pub enum BrokerStatus { @@ -12,25 +12,31 @@ pub enum BrokerStatus { None, Reachable, Unreachable(String), - ReachableVersion(String) + ReachableVersion(String), } impl BrokerStatus { pub fn reachable(&self) -> bool { match self { - Self::Reachable | - Self::ReachableVersion(_) => true, - Self::Unreachable(_) | - Self::None => false, + Self::Reachable | Self::ReachableVersion(_) => true, + Self::Unreachable(_) | Self::None => false, } } } +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub enum ValidatorStatus { + #[default] + Inactive, + Connecting, + RetrievingVersion, +} + #[derive(Default)] pub(crate) struct BrokerValidator { + status: ValidatorStatus, previous_broker_input: String, broker_status: BrokerStatus, - validation_in_progress: bool, last_input_change: Option, broker_validation_receiver: Option>, } @@ -40,8 +46,8 @@ impl BrokerValidator { &self.broker_status } - pub fn validation_in_progress(&self) -> bool { - self.validation_in_progress + pub fn status(&self) -> ValidatorStatus { + self.status } pub(crate) fn poll_broker_status(&mut self, ip: &str, port: &str) { @@ -56,65 +62,77 @@ impl BrokerValidator { // Debounce and validate after a timeout if let Some(last_change) = self.last_input_change { - if last_change.elapsed() >= Duration::from_millis(500) && !self.validation_in_progress { + if last_change.elapsed() >= Duration::from_millis(500) + && self.status() == ValidatorStatus::Inactive + { let (tx, rx) = std::sync::mpsc::channel(); self.broker_validation_receiver = Some(rx); - self.validation_in_progress = true; + self.status = ValidatorStatus::Connecting; self.last_input_change = None; - // Spawn validation thread - let (cp_host, cp_port) = (ip.to_owned(), port.to_owned()); - if let Err(e) = std::thread::Builder::new() - .name("broker-validator".into()) - .spawn(move || { - match validate_broker(&cp_host, &cp_port) { - Ok(addr) => { - // First send that it's reachable - if let Err(e) = tx.send(BrokerStatus::Reachable) { - log::error!("{e}"); - return; - } - - // Then try to get the version - match get_broker_version(addr) { - Ok(version) => { - if let Err(e) = tx.send(BrokerStatus::ReachableVersion(version)) { - log::error!("{e}"); - } - } - Err(e) => { - log::warn!("Failed to get broker version: {e}"); - // Keep the Reachable status since we at least know it's reachable - } - } - - }, - Err(e) => if let Err(e) = tx.send(BrokerStatus::Unreachable(e)) { - log::error!("{e}"); - }, - } - }) - { - log::error!("{e}"); - debug_assert!(false, "{e}"); - } + spawn_validation_thread((ip, port), tx); } } // Check for validation results, if we got a result we store the result and reset the check status if let Some(receiver) = &mut self.broker_validation_receiver { if let Ok(result) = receiver.try_recv() { - self.broker_status = result; // If the broker is reachable we continue so we can resolve its version - if self.broker_status != BrokerStatus::Reachable { - self.validation_in_progress = false; - self.broker_validation_receiver = None; + match result { + BrokerStatus::Reachable => self.status = ValidatorStatus::RetrievingVersion, + BrokerStatus::ReachableVersion(_) + | BrokerStatus::None + | BrokerStatus::Unreachable(_) => { + self.status = ValidatorStatus::Inactive; + self.broker_validation_receiver = None; + } } + self.broker_status = result; } } } } +fn spawn_validation_thread((ip, port): (&str, &str), tx: Sender) { + // Spawn validation thread + let (cp_host, cp_port) = (ip.to_owned(), port.to_owned()); + if let Err(e) = std::thread::Builder::new() + .name("broker-validator".into()) + .spawn(move || { + match validate_broker(&cp_host, &cp_port) { + Ok(addr) => { + // First send that it's reachable + if let Err(e) = tx.send(BrokerStatus::Reachable) { + log::error!("{e}"); + return; + } + + // Then try to get the version + match get_broker_version(addr) { + Ok(version) => { + if let Err(e) = tx.send(BrokerStatus::ReachableVersion(version)) { + log::error!("{e}"); + } + } + Err(e) => { + log::warn!("Failed to get broker version: {e}"); + // Keep the Reachable status since we at least know it's reachable + } + } + } + Err(e) => { + if let Err(e) = tx.send(BrokerStatus::Unreachable(e)) { + log::error!("{e}"); + } + } + } + }) + { + log::error!("{e}"); + debug_assert!(false, "{e}"); + } +} + fn validate_broker(host: &str, port: &str) -> Result { // Validate port first let port: u16 = port.parse().map_err(|e| format!("Invalid port: {e}"))?; @@ -163,7 +181,7 @@ fn get_broker_version(addr: SocketAddr) -> Result { // Wait for the version message with a timeout let start = Instant::now(); let timeout = Duration::from_secs(2); - + while start.elapsed() < timeout { match connection.iter().next() { Some(Ok(Event::Incoming(Packet::Publish(publish)))) => { diff --git a/crates/plotinator_mqtt/src/lib.rs b/crates/plotinator_mqtt/src/lib.rs index 4ec7712b..c3238357 100644 --- a/crates/plotinator_mqtt/src/lib.rs +++ b/crates/plotinator_mqtt/src/lib.rs @@ -1,6 +1,6 @@ use plotinator_macros::non_wasm_modules; non_wasm_modules!( - pub(crate) mod broker_validator; + pub mod broker_validator; pub(crate) mod mqtt_listener; pub(crate) mod topic_discoverer; pub(crate) mod parse_packet; diff --git a/crates/plotinator_mqtt/src/mqtt_cfg_window.rs b/crates/plotinator_mqtt/src/mqtt_cfg_window.rs index 46a05684..3f05cb6f 100644 --- a/crates/plotinator_mqtt/src/mqtt_cfg_window.rs +++ b/crates/plotinator_mqtt/src/mqtt_cfg_window.rs @@ -1,13 +1,14 @@ use std::{ collections::HashSet, sync::{ - atomic::{AtomicBool, Ordering}, Arc, + atomic::{AtomicBool, Ordering}, }, }; use crate::{ - broker_validator::{BrokerStatus, BrokerValidator}, data_receiver::MqttDataReceiver, + broker_validator::{BrokerStatus, BrokerValidator, ValidatorStatus}, + data_receiver::MqttDataReceiver, topic_discoverer::TopicDiscoverer, }; @@ -123,8 +124,8 @@ impl MqttConfigWindow { self.broker_validator.broker_status() } - pub fn validation_in_progress(&self) -> bool { - self.broker_validator.validation_in_progress() + pub fn validator_status(&self) -> ValidatorStatus { + self.broker_validator.status() } pub fn poll_broker_status(&mut self) { diff --git a/src/mqtt/mqtt_window.rs b/src/mqtt/mqtt_window.rs index 9e54e77c..d3dc0d37 100644 --- a/src/mqtt/mqtt_window.rs +++ b/src/mqtt/mqtt_window.rs @@ -2,7 +2,7 @@ use egui::Color32; use egui::RichText; use egui::ScrollArea; use egui::Ui; -use plotinator_mqtt::BrokerStatus; +use plotinator_mqtt::{BrokerStatus, broker_validator::ValidatorStatus}; use plotinator_mqtt::{MqttConfigWindow, MqttDataReceiver}; use crate::util::theme_color; @@ -47,13 +47,20 @@ fn show_broker_config_column(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow .on_hover_text("1883 is the default MQTT broker port"); }); - if mqtt_cfg_window.validation_in_progress() { - ui.horizontal(|ui| { - ui.spinner(); - ui.label("Checking for broker..."); - }); - } else { - show_broker_status(ui, mqtt_cfg_window.broker_status()); + match mqtt_cfg_window.validator_status() { + ValidatorStatus::Inactive => show_broker_status(ui, mqtt_cfg_window.broker_status()), + ValidatorStatus::Connecting => { + ui.horizontal(|ui| { + ui.spinner(); + ui.label("Checking for broker..."); + }); + } + ValidatorStatus::RetrievingVersion => { + ui.horizontal(|ui| { + ui.spinner(); + ui.label("Retrieving broker version..."); + }); + } } mqtt_cfg_window.poll_broker_status();