diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2ae5763f..1b859fe3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -78,7 +78,18 @@ 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 }} + if: matrix.os == 'macos-latest' + run: cargo nextest run --workspace --profile ci --run-ignored=all + + # 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: + name: test-results + path: "**/tests/snapshots" format: runs-on: ubuntu-latest 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/CHANGELOG.md b/CHANGELOG.md index 86e6a0d9..95af2ec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,25 @@ 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 + +### Changed + +- 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 +- Added GUI tests, including snapshot tests + ## [1.12.0] ### Changed diff --git a/Cargo.lock b/Cargo.lock index 4e2eb4ed..cde44a75 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" @@ -56,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]] @@ -84,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", @@ -186,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", ] @@ -240,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", + "zbus 5.6.0", ] [[package]] @@ -274,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", ] @@ -393,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", @@ -408,6 +507,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" @@ -490,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", @@ -547,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", ] @@ -580,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]] @@ -608,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", @@ -640,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" @@ -669,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", @@ -700,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", @@ -738,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", @@ -772,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", ] @@ -795,6 +945,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.59.0", +] + [[package]] name = "combine" version = "4.6.7" @@ -890,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", ] @@ -970,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", ] @@ -1016,6 +1176,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" @@ -1039,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]] @@ -1067,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", ] @@ -1082,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" @@ -1128,6 +1311,7 @@ dependencies = [ "objc2-foundation 0.2.2", "parking_lot", "percent-encoding", + "pollster", "profiling", "raw-window-handle", "ron", @@ -1137,6 +1321,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "web-time", + "wgpu", "winapi", "windows-sys 0.59.0", "winit", @@ -1151,7 +1336,7 @@ dependencies = [ "accesskit", "ahash", "backtrace", - "bitflags 2.8.0", + "bitflags 2.9.0", "emath", "epaint", "log", @@ -1205,6 +1390,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 +1423,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" @@ -1251,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" @@ -1377,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", @@ -1393,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" @@ -1410,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", @@ -1435,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", @@ -1462,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" @@ -1583,6 +1785,7 @@ dependencies = [ "futures-core", "futures-io", "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -1629,11 +1832,20 @@ 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" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", @@ -1644,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]] @@ -1699,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", @@ -1769,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", ] @@ -1779,7 +1991,19 @@ 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]] +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.58.0", ] [[package]] @@ -1788,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", ] @@ -1799,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", @@ -1823,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", ] @@ -1836,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", @@ -1888,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", @@ -1954,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", @@ -1975,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", @@ -1988,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" @@ -2022,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]] @@ -2048,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", @@ -2058,6 +2282,7 @@ dependencies = [ "http", "http-body", "hyper", + "libc", "pin-project-lite", "socket2", "tokio", @@ -2067,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]] @@ -2090,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", @@ -2113,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", @@ -2145,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" @@ -2219,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", @@ -2229,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", @@ -2240,11 +2434,20 @@ 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" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown", @@ -2252,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", ] @@ -2282,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", @@ -2301,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", @@ -2334,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", ] @@ -2374,6 +2578,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" @@ -2382,18 +2597,28 @@ 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]] +name = "libmimalloc-sys" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4" +dependencies = [ + "cc", + "libc", ] [[package]] @@ -2402,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]] @@ -2415,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" @@ -2441,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" @@ -2462,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" @@ -2538,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", @@ -2549,27 +2774,35 @@ 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", "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" @@ -2578,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", @@ -2605,7 +2838,7 @@ checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg_aliases", "codespan-reporting", "hexf-parse", @@ -2621,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", @@ -2657,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", @@ -2696,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", @@ -2790,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", ] @@ -2803,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", @@ -2815,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]] @@ -2831,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", @@ -2855,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", @@ -2863,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 = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.8.0", - "objc2 0.6.0", + "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 = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +dependencies = [ + "bitflags 2.9.0", + "dispatch2 0.3.0", + "objc2 0.6.1", + "objc2-core-foundation", + "objc2-io-surface", ] [[package]] @@ -2907,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", @@ -2916,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", ] @@ -2943,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", @@ -2955,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", @@ -2978,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", @@ -3010,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", @@ -3028,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" @@ -3038,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", @@ -3066,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", @@ -3137,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", ] @@ -3166,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", @@ -3209,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" @@ -3240,12 +3500,14 @@ dependencies = [ "egui", "egui-notify", "egui-phosphor", + "egui_kittest", "egui_plot", "elevated-command", "env_logger", "getset", "log", "log_if", + "mimalloc", "plot_util", "plotinator_macros", "plotinator_mqtt", @@ -3326,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" @@ -3339,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" @@ -3347,13 +3618,19 @@ 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]] +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" @@ -3366,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", ] @@ -3397,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", ] @@ -3456,43 +3733,56 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.37.2" +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.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", @@ -3503,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", @@ -3517,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" @@ -3537,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]] @@ -3572,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]] @@ -3581,9 +3876,15 @@ 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]] +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" @@ -3627,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]] @@ -3697,7 +3998,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.22", + "rustls 0.23.27", "rustls-pemfile", "rustls-pki-types", "serde", @@ -3707,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", ] @@ -3725,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", @@ -3744,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", @@ -3763,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", ] @@ -3780,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", @@ -3810,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", @@ -3819,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", ] @@ -3839,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", ] @@ -3882,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]] @@ -3900,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" @@ -3932,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", @@ -3944,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", @@ -3972,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", @@ -4060,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", @@ -4109,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", ] @@ -4185,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" @@ -4195,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", @@ -4236,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", @@ -4259,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]] @@ -4323,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", @@ -4343,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", @@ -4358,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", ] @@ -4375,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", ] @@ -4462,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", @@ -4475,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", @@ -4491,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", ] @@ -4506,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", @@ -4556,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", @@ -4579,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", @@ -4591,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" @@ -4693,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" @@ -4710,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" @@ -4756,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" @@ -4813,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", ] @@ -4893,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", @@ -4907,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", @@ -4923,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", @@ -4941,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", @@ -4953,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", @@ -4966,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", @@ -4984,7 +5298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.37.5", "quote", ] @@ -5022,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 = "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 = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" dependencies = [ "rustls-pki-types", ] @@ -5055,16 +5377,17 @@ 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", "log", + "naga", "parking_lot", "profiling", "raw-window-handle", @@ -5080,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", @@ -5105,20 +5428,23 @@ 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", - "bitflags 2.8.0", + "bit-set", + "bitflags 2.9.0", + "block", "bytemuck", "cfg_aliases", "core-graphics-types", "glow", "glutin_wgl_sys", "gpu-alloc", + "gpu-allocator", "gpu-descriptor", "js-sys", "khronos-egl", @@ -5133,6 +5459,7 @@ dependencies = [ "ordered-float", "parking_lot", "profiling", + "range-alloc", "raw-window-handle", "renderdoc-sys", "rustc-hash 1.1.0", @@ -5142,6 +5469,7 @@ dependencies = [ "web-sys", "wgpu-types", "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] @@ -5150,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", @@ -5158,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" @@ -5257,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" @@ -5279,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" @@ -5301,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" @@ -5313,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", ] @@ -5338,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", ] @@ -5364,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" @@ -5644,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", @@ -5695,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", ] @@ -5715,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" @@ -5788,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", @@ -5803,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" @@ -5824,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", @@ -5836,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", @@ -5848,9 +6214,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.4.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbddd8b6cb25d5d8ec1b23277b45299a98bfb220f1761ca11e186d5c702507f8" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" dependencies = [ "async-broadcast", "async-executor", @@ -5865,85 +6231,160 @@ dependencies = [ "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.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2522b82023923eecb0b366da727ec883ace092e7887b61d3da5139f26b44da58" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "nix", + "ordered-stream", + "serde", + "serde_repr", + "tracing", + "uds_windows", "windows-sys 0.59.0", "winnow", - "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 5.6.0", + "zbus_names 4.2.0", + "zvariant 5.5.1", +] + +[[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 = "5.4.0" +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]] +name = "zbus_macros" +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", - "zvariant", - "zvariant_utils", + "zbus_names 4.2.0", + "zvariant 5.5.1", + "zvariant_utils 3.2.0", ] [[package]] name = "zbus_names" -version = "4.2.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", - "winnow", - "zvariant", + "zvariant 4.2.0", ] [[package]] -name = "zerocopy" -version = "0.7.35" +name = "zbus_names" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", + "serde", + "static_assertions", + "winnow", + "zvariant 5.5.1", ] [[package]] -name = "zerocopy" -version = "0.8.23" +name = "zbus_xml" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +checksum = "ab3f374552b954f6abb4bd6ce979e6c9b38fb9d0cd7cc68a7d796e70c9f3a233" dependencies = [ - "zerocopy-derive 0.8.23", + "quick-xml 0.30.0", + "serde", + "static_assertions", + "zbus_names 3.0.0", + "zvariant 4.2.0", ] [[package]] -name = "zerocopy-derive" -version = "0.7.35" +name = "zerocopy" +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", @@ -5952,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", @@ -5991,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", @@ -6004,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", @@ -6027,7 +6479,7 @@ dependencies = [ "crossbeam-utils", "deflate64", "flate2", - "getrandom 0.3.1", + "getrandom 0.3.3", "hmac", "indexmap", "lzma-rs", @@ -6043,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", @@ -6085,31 +6535,67 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.3.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c951c21879c6e1d46ac5adfc34f698fefb465d498cf4ac87545849bd71bb5a" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" dependencies = [ "endi", "enumflags2", "serde", "static_assertions", + "zvariant_derive 4.2.0", +] + +[[package]] +name = "zvariant" +version = "5.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "557e89d54880377a507c94cd5452f20e35d14325faf9d2958ebeadce0966c1b2" +dependencies = [ + "endi", + "enumflags2", + "serde", "url", "winnow", - "zvariant_derive", - "zvariant_utils", + "zvariant_derive 5.5.1", + "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]] 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", "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 bc6465fe..a9d05253 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,12 +99,16 @@ 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] 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} @@ -171,7 +175,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/Justfile b/Justfile index c402d066..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 @@ -96,7 +100,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/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/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/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 diff --git a/crates/plotinator_mqtt/src/broker_validator.rs b/crates/plotinator_mqtt/src/broker_validator.rs index 244a8d84..7a1f419a 100644 --- a/crates/plotinator_mqtt/src/broker_validator.rs +++ b/crates/plotinator_mqtt/src/broker_validator.rs @@ -1,25 +1,53 @@ +use crate::util::timestamped_client_id; +use rumqttc::{Client, Event, MqttOptions, Packet}; use std::{ - net::{Ipv6Addr, TcpStream, ToSocketAddrs as _}, - sync::mpsc, + net::{Ipv6Addr, SocketAddr, TcpStream, ToSocketAddrs as _}, + sync::mpsc::{self, Sender}, time::{Duration, Instant}, }; +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub enum BrokerStatus { + #[default] + None, + Reachable, + Unreachable(String), + ReachableVersion(String), +} + +impl BrokerStatus { + pub fn reachable(&self) -> bool { + match self { + 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: Option>, - validation_in_progress: bool, + broker_status: BrokerStatus, 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 { - self.validation_in_progress + pub fn status(&self) -> ValidatorStatus { + self.status } pub(crate) fn poll_broker_status(&mut self, ip: &str, port: &str) { @@ -29,46 +57,83 @@ 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 { + 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 || { - let result = validate_broker(&cp_host, &cp_port); - if let Err(e) = tx.send(result) { - 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 = Some(result); - self.validation_in_progress = false; - self.broker_validation_receiver = None; + // If the broker is reachable we continue so we can resolve its version + 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 validate_broker(host: &str, port: &str) -> Result<(), String> { +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}"))?; @@ -91,7 +156,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 +166,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, + _ => (), + } + } + + Err("Timeout waiting for broker version".to_owned()) +} diff --git a/crates/plotinator_mqtt/src/lib.rs b/crates/plotinator_mqtt/src/lib.rs index f5c215e1..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; @@ -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..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::BrokerValidator, data_receiver::MqttDataReceiver, + broker_validator::{BrokerStatus, BrokerValidator, ValidatorStatus}, + data_receiver::MqttDataReceiver, topic_discoverer::TopicDiscoverer, }; @@ -27,6 +28,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,12 +112,20 @@ impl MqttConfigWindow { self.topic_discoverer.discovered_topics_sorted() } - pub fn broker_status(&self) -> Option<&Result<(), String>> { + 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() } - 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/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/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..62163ff4 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) } } } @@ -140,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/plotinator_mqtt/src/topic_discoverer.rs b/crates/plotinator_mqtt/src/topic_discoverer.rs index 62a69d0c..3c12610c 100644 --- a/crates/plotinator_mqtt/src/topic_discoverer.rs +++ b/crates/plotinator_mqtt/src/topic_discoverer.rs @@ -8,10 +8,13 @@ use std::{ time::Duration, }; +use crate::util::timestamped_client_id; + #[derive(Default)] pub(crate) struct TopicDiscoverer { active: bool, discovered_topics: HashSet, + discovered_sys_topics: HashSet, discovery_rx: Option>, stop_discovery_flag: Arc, discovery_handle: Option>, @@ -41,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), }; } @@ -72,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), } @@ -88,10 +103,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}" @@ -108,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!"); @@ -118,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}"); } } @@ -139,3 +164,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 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 { diff --git a/just/ci.just b/just/ci.just index 9d13683a..c5173d8b 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 diff --git a/src/app.rs b/src/app.rs index 9dd21c7b..d7f47dc0 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,27 +133,9 @@ 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(), - #[cfg(all(not(target_arch = "wasm32"), feature = "mqtt"))] - &mut self.set_auto_bounds, + &mut self.mqtt, ); - #[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(); - }); - } - } if self.plot.plot_count() == 0 { // Display the message when plots are shown util::draw_empty_state(ui); @@ -242,8 +209,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,15 +227,12 @@ 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!( - "{} Open File", - egui_phosphor::regular::FOLDER_OPEN + "{icon} Open File", + icon = egui_phosphor::regular::FOLDER_OPEN ))) .clicked() { @@ -306,27 +270,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/main.rs b/src/main.rs index bcb529d4..af9f7902 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,10 @@ #![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, frames rendered ~25% faster on windows 11 + // When compiling natively: #[cfg(not(target_arch = "wasm32"))] fn main() -> eframe::Result { diff --git a/src/mqtt.rs b/src/mqtt.rs new file mode 100644 index 00000000..57fd3dd5 --- /dev/null +++ b/src/mqtt.rs @@ -0,0 +1,97 @@ +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.add_space(20.); + ui.vertical_centered_justified(|ui| { + 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); + } + 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/mqtt_window.rs b/src/mqtt/mqtt_window.rs new file mode 100644 index 00000000..d3dc0d37 --- /dev/null +++ b/src/mqtt/mqtt_window.rs @@ -0,0 +1,259 @@ +use egui::Color32; +use egui::RichText; +use egui::ScrollArea; +use egui::Ui; +use plotinator_mqtt::{BrokerStatus, broker_validator::ValidatorStatus}; +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, + mqtt_cfg_window_open: &mut bool, + mqtt_cfg_window: &mut MqttConfigWindow, +) -> Option { + let mut data_receiver: Option = None; + let mut connect_clicked = false; + egui::Window::new("MQTT Configuration") + .open(mqtt_cfg_window_open) + .scroll([false, true]) + .show(ctx, |ui| { + ui.columns(2, |columns| { + 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() { + mqtt_cfg_window.stop_topic_discovery(); + } + data_receiver +} + +fn show_broker_config_column(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { + 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"); + }); + + 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(); + + 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| { + 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 => (), + 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!( + "{} Stop Discovery", + egui_phosphor::regular::CELL_TOWER + )) + .clicked() + { + mqtt_cfg_window.stop_topic_discovery(); + } + // Show discovery status + ui.horizontal(|ui| { + ui.spinner(); + ui.colored_label( + theme_color(ui, Color32::CYAN, Color32::BLUE), + "Discovering topics...", + ); + }); + + // Process incoming topics + if let Err(e) = mqtt_cfg_window.poll_discovered_topics() { + ui.colored_label(Color32::RED, e); + } +} + +fn show_subscribed_topics(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { + let subscribed_topics = mqtt_cfg_window.selected_topics().len(); + let label_txt = if subscribed_topics == 0 { + RichText::new("Select topics to subscribe to before connecting").color(theme_color( + ui, + Color32::YELLOW, + Color32::ORANGE, + )) + } else { + RichText::new(format!("Subscribed Topics ({subscribed_topics}):")) + }; + ui.label(label_txt); + for topic in mqtt_cfg_window.selected_topics_as_mut() { + ui.horizontal(|ui| { + if ui + .button(RichText::new(egui_phosphor::regular::TRASH)) + .clicked() + { + // Make them an empty string and then cleanup empty strings after the loop + topic.clear(); + } else { + ui.label(topic.clone()); + } + }); + } + mqtt_cfg_window.remove_empty_selected_topics(); +} + +fn show_discovered_topics_list( + ui: &mut Ui, + mqtt_cfg_window: &mut MqttConfigWindow, + topics: &[String], +) { + 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(), + ); + }, + ); + } +} diff --git a/src/mqtt_window.rs b/src/mqtt_window.rs deleted file mode 100644 index 2c4c10b8..00000000 --- a/src/mqtt_window.rs +++ /dev/null @@ -1,166 +0,0 @@ -use egui::Color32; -use egui::RichText; -use egui::ScrollArea; -use egui::Ui; -use plotinator_mqtt::{MqttConfigWindow, MqttDataReceiver}; - -/// Shows the MQTT configuration window and returns a receiver channel if connect was clicked -pub fn show_mqtt_window( - ctx: &egui::Context, - mqtt_cfg_window_open: &mut bool, - mqtt_cfg_window: &mut MqttConfigWindow, -) -> Option { - let mut data_receiver: Option = None; - let mut connect_clicked = false; - egui::Window::new("MQTT Configuration") - .open(mqtt_cfg_window_open) - .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 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().is_some_and(|s| s.is_ok()) - && !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(200.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()); - } - }); - } - }); - } - }); - show_subscribed_topics(ui, mqtt_cfg_window); - - if ui.button("Connect").clicked() { - connect_clicked = true; - data_receiver = Some(mqtt_cfg_window.spawn_mqtt_listener()); - } - }); - // 4. Cleanup when window closes - if (!*mqtt_cfg_window_open || connect_clicked) && mqtt_cfg_window.discovery_active() { - mqtt_cfg_window.stop_topic_discovery(); - } - 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_active_discovery_status(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { - if ui - .button(format!( - "{} Stop Discovery", - egui_phosphor::regular::CELL_TOWER - )) - .clicked() - { - mqtt_cfg_window.stop_topic_discovery(); - } - // Show discovery status - ui.horizontal(|ui| { - ui.spinner(); - ui.colored_label(Color32::BLUE, "Discovering topics..."); - }); - - // Process incoming topics - if let Err(e) = mqtt_cfg_window.poll_discovered_topics() { - ui.colored_label(Color32::RED, e); - } -} - -fn show_subscribed_topics(ui: &mut Ui, mqtt_cfg_window: &mut MqttConfigWindow) { - if !mqtt_cfg_window.selected_topics().is_empty() { - ui.label("Subscribed Topics:"); - } - for topic in mqtt_cfg_window.selected_topics_as_mut() { - ui.horizontal(|ui| { - if ui - .button(RichText::new(egui_phosphor::regular::TRASH)) - .clicked() - { - // Make them an empty string and then cleanup empty strings after the loop - topic.clear(); - } else { - ui.label(topic.clone()); - } - }); - } - mqtt_cfg_window.remove_empty_selected_topics(); -} 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); 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/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; } 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 } } diff --git a/src/util.rs b/src/util.rs index b2718e18..7bfd2448 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,12 @@ 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, + egui::Theme::Light => light, + } +} diff --git a/tests/snapshots/default_app_window.png b/tests/snapshots/default_app_window.png new file mode 100644 index 00000000..46fee748 Binary files /dev/null and b/tests/snapshots/default_app_window.png differ diff --git a/tests/snapshots/default_mqtt_config_window.png b/tests/snapshots/default_mqtt_config_window.png new file mode 100644 index 00000000..2e476643 Binary files /dev/null and b/tests/snapshots/default_mqtt_config_window.png differ 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 00000000..c99587e0 Binary files /dev/null and b/tests/snapshots/dropped_mbed_pid_regular_v6.png differ 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 00000000..547a5301 Binary files /dev/null and b/tests/snapshots/dropped_mbed_status_regular_v6.png differ diff --git a/tests/test_snapshot_plotinator3000.rs b/tests/test_snapshot_plotinator3000.rs new file mode 100644 index 00000000..1447be10 --- /dev/null +++ b/tests/test_snapshot_plotinator3000.rs @@ -0,0 +1,63 @@ +mod util; +use test_util::{mbed_pid_v6_regular, mbed_status_v6_regular}; +use util::*; + +#[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(); + + // 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 new file mode 100644 index 00000000..008d63fe --- /dev/null +++ b/tests/util/mod.rs @@ -0,0 +1,115 @@ +#![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, + 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)) +} + +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>, +} + +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.save_snapshot_with_threshold(CiThreshold::default()); + } + + /// Save a named snapshot, ensure that contents are fitted before taking the snapshot + /// + /// [`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, + /// 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 { + let opt = egui_kittest::SnapshotOptions::new().threshold(threshold.0); + self.harness.snapshot_options(&self.name, &opt); + } + } else { + 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") + } +}