From cae50076c3cd3d6f9053c266819faafcaccb4a6e Mon Sep 17 00:00:00 2001 From: Grigorios Mallios Date: Sun, 15 Oct 2023 11:41:51 +0300 Subject: [PATCH] wip: parsing using nom --- .gitignore | 95 +- Cargo.toml | 9 + Readme.md | 9 +- bluetooth-lib/src/winrt/rfcomm.rs | 1 - package.json | 4 +- soundcore-lib/Cargo.lock | 904 ++++++++++-------- soundcore-lib/Cargo.toml | 9 +- soundcore-lib/Decompile.md | 10 +- soundcore-lib/examples/basic.rs | 2 +- soundcore-lib/src/api.rs | 3 + soundcore-lib/src/api/state.rs | 3 + soundcore-lib/src/api/state/state.rs | 26 + soundcore-lib/src/devices/A3951Device.rs | 16 +- soundcore-lib/src/lib.rs | 6 +- soundcore-lib/src/models.rs | 51 + .../src/models/a3909_button_model.rs | 131 +++ soundcore-lib/src/models/age.rs | 16 + soundcore-lib/src/models/anc_mode.rs | 63 ++ soundcore-lib/src/models/auto_power.rs | 8 + soundcore-lib/src/models/battery.rs | 49 + soundcore-lib/src/models/button_model.rs | 8 + soundcore-lib/src/models/curr_sound_mode.rs | 63 ++ soundcore-lib/src/models/custom_anc.rs | 19 + soundcore-lib/src/models/eq.rs | 71 ++ soundcore-lib/src/models/eq_configuration.rs | 20 + soundcore-lib/src/models/eq_profile.rs | 86 ++ soundcore-lib/src/models/feature_flags.rs | 25 + soundcore-lib/src/models/fw.rs | 57 ++ soundcore-lib/src/models/game_mode.rs | 6 + soundcore-lib/src/models/gender.rs | 16 + soundcore-lib/src/models/hearid.rs | 30 + soundcore-lib/src/models/packet_header.rs | 9 + soundcore-lib/src/models/packet_kind.rs | 18 + soundcore-lib/src/models/serial.rs | 23 + soundcore-lib/src/models/side_tone.rs | 12 + soundcore-lib/src/models/sound_mode.rs | 25 + soundcore-lib/src/models/touch_tone.rs | 12 + soundcore-lib/src/models/trans_mode.rs | 60 ++ soundcore-lib/src/models/tws_status.rs | 12 + soundcore-lib/src/models/wear_detection.rs | 12 + soundcore-lib/src/packets.rs | 3 + soundcore-lib/src/packets/response.rs | 30 + soundcore-lib/src/packets/response/info.rs | 12 + .../src/packets/response/info/a3951.rs | 0 soundcore-lib/src/packets/response/state.rs | 44 + .../src/packets/response/state/a3951.rs | 191 ++++ soundcore-lib/src/parsers.rs | 45 + .../src/parsers/a3909_button_model.rs | 116 +++ soundcore-lib/src/parsers/age.rs | 11 + soundcore-lib/src/parsers/auto_power.rs | 16 + soundcore-lib/src/parsers/base.rs | 104 ++ soundcore-lib/src/parsers/battery.rs | 82 ++ soundcore-lib/src/parsers/checksum.rs | 52 + soundcore-lib/src/parsers/eq.rs | 75 ++ soundcore-lib/src/parsers/eq_configuration.rs | 55 ++ soundcore-lib/src/parsers/game_mode.rs | 0 soundcore-lib/src/parsers/gender.rs | 13 + soundcore-lib/src/parsers/hearid.rs | 72 ++ soundcore-lib/src/parsers/packet_header.rs | 114 +++ soundcore-lib/src/parsers/serial.rs | 23 + soundcore-lib/src/parsers/side_tone.rs | 12 + soundcore-lib/src/parsers/sound_mode.rs | 92 ++ soundcore-lib/src/parsers/touch_tone.rs | 9 + soundcore-lib/src/parsers/wear_detection.rs | 11 + soundcore-lib/tests/a3951.rs | 20 + src-tauri/Cargo.lock | 84 +- src-tauri/build.rs | 2 +- src-tauri/src/main.rs | 3 +- 68 files changed, 2829 insertions(+), 461 deletions(-) create mode 100644 Cargo.toml create mode 100644 soundcore-lib/src/api.rs create mode 100644 soundcore-lib/src/api/state.rs create mode 100644 soundcore-lib/src/api/state/state.rs create mode 100644 soundcore-lib/src/models.rs create mode 100644 soundcore-lib/src/models/a3909_button_model.rs create mode 100644 soundcore-lib/src/models/age.rs create mode 100644 soundcore-lib/src/models/anc_mode.rs create mode 100644 soundcore-lib/src/models/auto_power.rs create mode 100644 soundcore-lib/src/models/battery.rs create mode 100644 soundcore-lib/src/models/button_model.rs create mode 100644 soundcore-lib/src/models/curr_sound_mode.rs create mode 100644 soundcore-lib/src/models/custom_anc.rs create mode 100644 soundcore-lib/src/models/eq.rs create mode 100644 soundcore-lib/src/models/eq_configuration.rs create mode 100644 soundcore-lib/src/models/eq_profile.rs create mode 100644 soundcore-lib/src/models/feature_flags.rs create mode 100644 soundcore-lib/src/models/fw.rs create mode 100644 soundcore-lib/src/models/game_mode.rs create mode 100644 soundcore-lib/src/models/gender.rs create mode 100644 soundcore-lib/src/models/hearid.rs create mode 100644 soundcore-lib/src/models/packet_header.rs create mode 100644 soundcore-lib/src/models/packet_kind.rs create mode 100644 soundcore-lib/src/models/serial.rs create mode 100644 soundcore-lib/src/models/side_tone.rs create mode 100644 soundcore-lib/src/models/sound_mode.rs create mode 100644 soundcore-lib/src/models/touch_tone.rs create mode 100644 soundcore-lib/src/models/trans_mode.rs create mode 100644 soundcore-lib/src/models/tws_status.rs create mode 100644 soundcore-lib/src/models/wear_detection.rs create mode 100644 soundcore-lib/src/packets.rs create mode 100644 soundcore-lib/src/packets/response.rs create mode 100644 soundcore-lib/src/packets/response/info.rs create mode 100644 soundcore-lib/src/packets/response/info/a3951.rs create mode 100644 soundcore-lib/src/packets/response/state.rs create mode 100644 soundcore-lib/src/packets/response/state/a3951.rs create mode 100644 soundcore-lib/src/parsers.rs create mode 100644 soundcore-lib/src/parsers/a3909_button_model.rs create mode 100644 soundcore-lib/src/parsers/age.rs create mode 100644 soundcore-lib/src/parsers/auto_power.rs create mode 100644 soundcore-lib/src/parsers/base.rs create mode 100644 soundcore-lib/src/parsers/battery.rs create mode 100644 soundcore-lib/src/parsers/checksum.rs create mode 100644 soundcore-lib/src/parsers/eq.rs create mode 100644 soundcore-lib/src/parsers/eq_configuration.rs create mode 100644 soundcore-lib/src/parsers/game_mode.rs create mode 100644 soundcore-lib/src/parsers/gender.rs create mode 100644 soundcore-lib/src/parsers/hearid.rs create mode 100644 soundcore-lib/src/parsers/packet_header.rs create mode 100644 soundcore-lib/src/parsers/serial.rs create mode 100644 soundcore-lib/src/parsers/side_tone.rs create mode 100644 soundcore-lib/src/parsers/sound_mode.rs create mode 100644 soundcore-lib/src/parsers/touch_tone.rs create mode 100644 soundcore-lib/src/parsers/wear_detection.rs create mode 100644 soundcore-lib/tests/a3951.rs diff --git a/.gitignore b/.gitignore index a547bf3..1a44822 100644 --- a/.gitignore +++ b/.gitignore @@ -4,21 +4,86 @@ logs npm-debug.log* yarn-debug.log* yarn-error.log* -pnpm-debug.log* -lerna-debug.log* -node_modules -dist -dist-ssr -*.local +# smoke-tests repo +/smoke-tests -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional yarn cache directory +.yarn + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +/.vs .DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? +.Thumbs.db +*.sublime* +.idea +debug.log +package-lock.json +.vscode/settings.json +*/.vscode/ +proptest-regressions/ +TODO.md + +# rust compiled folders +target + +# lock for libs +/Cargo.lock +/tooling/bench/tests/Cargo.lock +/yarn.lock + +# ignore frida handlers +__handlers__/ + +# benches +gh-pages +test_video.mp4 + +# old cli directories +/tooling/cli.js +/tooling/cli.rs \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c6862e4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +resolver = "2" +members = [ + "src-tauri/", + "soundcore-lib" +] + +[profile.release] +lto = true diff --git a/Readme.md b/Readme.md index cae35c9..2e5703b 100644 --- a/Readme.md +++ b/Readme.md @@ -1,14 +1,14 @@ Tauri

- A desktop companion app for your Soundcore devices + A desktop companion app for your Soundcore devices


- -![Github Actions](https://github.com/gmallios/SoundcoreManager/actions/workflows/push.yml/badge.svg) + +![Github Actions](https://github.com/gmallios/SoundcoreManager/actions/workflows/push.yml/badge.svg) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?hosted_button_id=58VZ5TZFZXACJ) - +
# Downloads @@ -40,6 +40,7 @@ | A3033 | Live 2 Neo | | A3931 | Life Dot 2 | | A3992 | Soundcore A3i | +| A3993 | Soundcore P3i | # Build Instructions diff --git a/bluetooth-lib/src/winrt/rfcomm.rs b/bluetooth-lib/src/winrt/rfcomm.rs index e342a21..d17f7da 100644 --- a/bluetooth-lib/src/winrt/rfcomm.rs +++ b/bluetooth-lib/src/winrt/rfcomm.rs @@ -8,7 +8,6 @@ use windows::{ BluetoothCacheMode, Rfcomm::{RfcommDeviceService, RfcommServiceId}, }, - Enumeration::DeviceInformation, }, Networking::Sockets::StreamSocket, Storage::Streams::{DataReader, DataWriter, InputStreamOptions}, diff --git a/package.json b/package.json index b03461d..80b698c 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@mui/material": "^5.12.0", "@tanstack/react-query": "^4.20.4", "@tanstack/react-query-devtools": "^4.20.4", - "@tauri-apps/api": "^1.2.0", + "@tauri-apps/api": "^1.5.0", "chart.js": "~3.9.1", "chartjs-plugin-dragdata": "^2.2.4", "react": "^18.2.0", @@ -31,7 +31,7 @@ "zustand": "^4.1.3" }, "devDependencies": { - "@tauri-apps/cli": "^1.2.0", + "@tauri-apps/cli": "^1.5.1", "@types/node": "^18.7.10", "@types/react": "^18.2.6", "@types/react-dom": "^18.0.6", diff --git a/soundcore-lib/Cargo.lock b/soundcore-lib/Cargo.lock index 7a6fee0..8537241 100644 --- a/soundcore-lib/Cargo.lock +++ b/soundcore-lib/Cargo.lock @@ -2,12 +2,36 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -19,40 +43,41 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "async-stream" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", + "pin-project-lite", ] [[package]] name = "async-stream-impl" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 1.0.103", + "syn 2.0.38", ] [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.38", ] [[package]] @@ -63,13 +88,13 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.1" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08b108ad2665fa3f6e6a517c3d80ec3e77d224c47d605167aefaa5d7ef97fa48" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "http", @@ -85,16 +110,15 @@ dependencies = [ "serde", "sync_wrapper", "tower", - "tower-http", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.3.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b8558f5a0581152dc94dcd289132a1d377494bdeafcd41869b3258e3e2ad92" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", "bytes", @@ -107,18 +131,45 @@ dependencies = [ "tower-service", ] +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "block" version = "0.1.6" @@ -139,21 +190,24 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytes" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.77" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -163,16 +217,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "wasm-bindgen", - "winapi", + "windows-targets 0.48.5", ] [[package]] @@ -181,7 +235,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "cocoa-foundation", "core-foundation", @@ -193,40 +247,29 @@ dependencies = [ [[package]] name = "cocoa-foundation" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "core-foundation", "core-graphics-types", - "foreign-types", "libc", "objc", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "console" -version = "0.15.5" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -241,9 +284,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core-graphics" @@ -251,7 +294,7 @@ version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-graphics-types", "foreign-types", @@ -260,13 +303,12 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", - "foreign-types", "libc", ] @@ -280,83 +322,84 @@ dependencies = [ ] [[package]] -name = "cxx" -version = "1.0.91" +name = "dialoguer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", + "console", + "fuzzy-matcher", + "shell-words", + "tempfile", + "zeroize", ] [[package]] -name = "cxx-build" -version = "1.0.91" +name = "either" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 1.0.103", -] +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] -name = "cxxbridge-flags" -version = "1.0.91" +name = "encode_unicode" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] -name = "cxxbridge-macro" -version = "1.0.91" +name = "enumflags2" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.103", + "enumflags2_derive", + "serde", ] [[package]] -name = "dialoguer" -version = "0.10.4" +name = "enumflags2_derive" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" +checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" dependencies = [ - "console", - "fuzzy-matcher", - "shell-words", - "tempfile", - "zeroize", + "proc-macro2", + "quote", + "syn 2.0.38", ] [[package]] -name = "either" -version = "1.8.0" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "encode_unicode" -version = "0.3.6" +name = "errno" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] [[package]] -name = "fastrand" -version = "1.8.0" +name = "errno-dragonfly" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ - "instant", + "cc", + "libc", ] +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "fixedbitset" version = "0.4.2" @@ -365,9 +408,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" dependencies = [ "crc32fast", "miniz_oxide", @@ -396,9 +439,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "futures" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -411,9 +454,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -421,15 +464,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -438,38 +481,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 1.0.103", + "syn 2.0.38", ] [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -494,20 +537,26 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", "wasi", ] +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "h2" -version = "0.3.17" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -515,7 +564,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -528,26 +577,38 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" + [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "libc", + "windows-sys 0.48.0", ] [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -565,12 +626,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-range-header" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" - [[package]] name = "httparse" version = "1.8.0" @@ -579,15 +634,15 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -600,7 +655,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -621,45 +676,45 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] -name = "instant" -version = "0.1.12" +name = "indexmap" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ - "cfg-if", + "equivalent", + "hashbrown 0.14.1", ] [[package]] @@ -694,15 +749,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -715,27 +770,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.138" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] -name = "link-cplusplus" -version = "1.0.8" +name = "linux-raw-sys" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] +checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "malloc_buf" @@ -748,41 +797,46 @@ dependencies = [ [[package]] name = "matchit" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -792,29 +846,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] -name = "num-integer" -version = "0.1.45" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "autocfg", - "num-traits", + "memchr", + "minimal-lexical", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", @@ -829,33 +883,42 @@ dependencies = [ "malloc_buf", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.16.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.0.2", ] [[package]] name = "phf" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ "phf_macros", "phf_shared", @@ -863,9 +926,9 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ "phf_shared", "rand", @@ -873,51 +936,51 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ "phf_generator", "phf_shared", "proc-macro2", "quote", - "syn 1.0.103", + "syn 2.0.38", ] [[package]] name = "phf_shared" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ "siphasher", ] [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 1.0.103", + "syn 2.0.38", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -933,28 +996,28 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" -version = "0.1.21" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c142c0e46b57171fe0c528bee8c5b7569e80f0c17e377cd0e30ea57dbc11bb51" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ "proc-macro2", - "syn 1.0.103", + "syn 1.0.109", ] [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.11.3" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b18e655c21ff5ac2084a5ad0611e827b3f92badf79f4910b5a5c58f4d87ff0" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", "prost-derive", @@ -962,9 +1025,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.4" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276470f7f281b0ed53d2ae42dd52b4a8d08853a3c70e7fe95882acbb98a6ae94" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", "heck", @@ -977,39 +1040,38 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 1.0.103", + "syn 1.0.109", "tempfile", "which", ] [[package]] name = "prost-derive" -version = "0.11.2" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164ae68b6587001ca506d3bf7f1000bfa248d0e1217b618108fba4ec1d0cc306" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", - "syn 1.0.103", + "syn 1.0.109", ] [[package]] name = "prost-types" -version = "0.11.2" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747761bc3dc48f9a34553bf65605cf6cb6288ba219f3450b4275dbd81539551a" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "bytes", "prost", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1046,36 +1108,41 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.7.0" +version = "1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", "regex-syntax", ] [[package]] -name = "regex-syntax" -version = "0.6.28" +name = "regex-automata" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-syntax" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "ring" @@ -1092,11 +1159,30 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", "ring", @@ -1106,30 +1192,24 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64", + "base64 0.21.4", ] [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" - -[[package]] -name = "scratch" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5e082f6ea090deaf0e6dd04b68360fd5cddb152af6ce8927c9d25db299f98c" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "sct" @@ -1143,29 +1223,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.162" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.162" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.38", ] [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -1180,29 +1260,39 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "siphasher" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", ] +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "soundcore-lib" version = "0.1.0" @@ -1210,9 +1300,12 @@ dependencies = [ "async-trait", "bluetooth-lib", "dialoguer", + "enumflags2", "log", + "nom", "phf", "serde", + "strum", "thiserror", "tokio", "typeshare", @@ -1224,11 +1317,33 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.38", +] + [[package]] name = "syn" -version = "1.0.103" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -1237,9 +1352,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -1248,51 +1363,41 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "tempfile" -version = "3.3.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", + "rustix", + "windows-sys 0.48.0", ] [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.38", ] [[package]] @@ -1307,20 +1412,19 @@ dependencies = [ [[package]] name = "tokio" -version = "1.26.0" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", "pin-project-lite", - "socket2", + "socket2 0.5.4", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1335,13 +1439,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.103", + "syn 2.0.38", ] [[package]] @@ -1357,9 +1461,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -1368,9 +1472,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", @@ -1389,7 +1493,7 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64", + "base64 0.13.1", "bytes", "flate2", "futures-core", @@ -1425,7 +1529,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 1.0.103", + "syn 1.0.109", ] [[package]] @@ -1436,7 +1540,7 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "indexmap", + "indexmap 1.9.3", "pin-project", "pin-project-lite", "rand", @@ -1448,25 +1552,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tower-http" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-layer" version = "0.3.2" @@ -1494,20 +1579,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 1.0.103", + "syn 2.0.38", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] @@ -1524,9 +1609,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typeshare" @@ -1547,20 +1632,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc670d0e358428857cc3b4bf504c691e572fccaec9542ff09212d3f13d74b7a9" dependencies = [ "quote", - "syn 1.0.103", + "syn 1.0.109", ] [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "untrusted" @@ -1570,11 +1655,10 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -1586,9 +1670,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1596,24 +1680,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.103", + "syn 2.0.38", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1621,28 +1705,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.103", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -1650,9 +1734,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.0" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "07ecc0cd7cac091bf682ec5efa18b1cff79d617b84181f38b3951dbe135f607f" dependencies = [ "ring", "untrusted", @@ -1660,13 +1744,14 @@ dependencies = [ [[package]] name = "which" -version = "4.3.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] @@ -1685,15 +1770,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1706,31 +1782,25 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.5", ] [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.42.2", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.48.5", ] [[package]] @@ -1750,17 +1820,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1771,9 +1841,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" @@ -1783,9 +1853,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" @@ -1795,9 +1865,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" @@ -1807,9 +1877,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" @@ -1819,9 +1889,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" @@ -1831,9 +1901,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" @@ -1843,12 +1913,12 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/soundcore-lib/Cargo.toml b/soundcore-lib/Cargo.toml index a5ec2af..f6e9251 100644 --- a/soundcore-lib/Cargo.toml +++ b/soundcore-lib/Cargo.toml @@ -8,9 +8,12 @@ name = "soundcore-lib-cli-tool" path = "src/cli.rs" [dependencies] -serde = { version = "1.0", features = ["derive"] } -tokio = { version = "1.26.0", features = ["time", "macros", "rt-multi-thread"] } -async-trait = "0.1.68" +serde = { version = "1", features = ["derive", "rc"] } +tokio = { version = "1", features = ["time", "macros", "rt-multi-thread"] } +strum = { version = "0.25", features = ["derive"] } +nom = "7" +enumflags2 = { version = "0.7.7", features = ["serde"] } +async-trait = "0.1" thiserror = "1.0.40" bluetooth-lib = { path = "../bluetooth-lib/" } log = "0.4.17" diff --git a/soundcore-lib/Decompile.md b/soundcore-lib/Decompile.md index f5cf22a..2de78aa 100644 --- a/soundcore-lib/Decompile.md +++ b/soundcore-lib/Decompile.md @@ -1,8 +1,10 @@ # Important classes + - A3951CmdConstants ( Has all opcodes ) - C7577b # ANC byte arrays from frida + ``` Normal Mode - 8,-18,0,0,0,6,-127,14,0,2,0,0,6,-109 @@ -16,10 +18,14 @@ Noise Cancellation, Custom(Max) - 8,-18,0,0,0,6,-127,14,0,0,3,1,10,-103 Noise Cancellation, Custom(Min) - 8,-18,0,0,0,6,-127,14,0,0,3,1,0,-113 ``` - # On Press MenuItem from frida + ``` Sends 8,-18,0,0,0,1,1,10,0,2 8,-18,0,0,0,1,5,10,0,6 -``` \ No newline at end of file +``` + +# TODO +- A3951DeviceManager Missing devices +- A3933DeviceManager \ No newline at end of file diff --git a/soundcore-lib/examples/basic.rs b/soundcore-lib/examples/basic.rs index 2de6930..6927cde 100644 --- a/soundcore-lib/examples/basic.rs +++ b/soundcore-lib/examples/basic.rs @@ -8,7 +8,7 @@ async fn main() { let addr = BluetoothAdrr::from(BT_ADDR); let dev = A3951::default().init(addr).await.unwrap(); let info = dev.get_status().await.unwrap(); - println!("Device info: {:?}", info); + println!("Device status: {:?}", info); // let charging = dev.get_battery_charging().await.unwrap(); // println!("Charging: {:?}", charging); } diff --git a/soundcore-lib/src/api.rs b/soundcore-lib/src/api.rs new file mode 100644 index 0000000..06b8a47 --- /dev/null +++ b/soundcore-lib/src/api.rs @@ -0,0 +1,3 @@ +mod state; + +pub use state::*; diff --git a/soundcore-lib/src/api/state.rs b/soundcore-lib/src/api/state.rs new file mode 100644 index 0000000..06b8a47 --- /dev/null +++ b/soundcore-lib/src/api/state.rs @@ -0,0 +1,3 @@ +mod state; + +pub use state::*; diff --git a/soundcore-lib/src/api/state/state.rs b/soundcore-lib/src/api/state/state.rs new file mode 100644 index 0000000..ec1568c --- /dev/null +++ b/soundcore-lib/src/api/state/state.rs @@ -0,0 +1,26 @@ +use enumflags2::BitFlags; +use serde::{Deserialize, Serialize}; + +use crate::models::{ + AgeRange, Battery, ButtonModel, CustomHearID, FirmwareVer, SerialNumber, SideTone, SoundMode, + SoundcoreFeatureFlags, TwsStatus, WearDetection, +}; +/// This is a generalized version of the state for all devices +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Hash, Default)] +pub struct SoundcoreDeviceState { + pub feature_flags: BitFlags, + pub battery: Battery, + pub sound_mode: SoundMode, + pub serial: Option, + pub left_fw: Option, + pub right_fw: Option, + pub drc_fw: Option, + pub host_device: Option, + pub tws_status: Option, + pub button_model: Option, + pub side_tone: Option, + pub hearid_eq_preset: Option, + pub wear_detection: Option, + pub hear_id: Option, + pub age_range: Option, +} diff --git a/soundcore-lib/src/devices/A3951Device.rs b/soundcore-lib/src/devices/A3951Device.rs index 52ad324..dce3146 100644 --- a/soundcore-lib/src/devices/A3951Device.rs +++ b/soundcore-lib/src/devices/A3951Device.rs @@ -255,7 +255,6 @@ impl ResponseDecoder for A3951 { if arr.len() < 93 { return Err(SoundcoreError::RecvError); } - Ok(DeviceStatus { host_device: arr[9], tws_status: arr[10] == 1, @@ -396,3 +395,18 @@ impl ANCProfile { ] } } + +#[cfg(test)] +impl A3951 { + /* Used for comparing in testing */ + pub fn device_status(bytes: &[u8]) -> DeviceStatus { + Self::decode( + &A3951 { + _btaddr: None, + rfcomm: None, + }, + bytes, + ) + .unwrap() + } +} diff --git a/soundcore-lib/src/lib.rs b/soundcore-lib/src/lib.rs index 569bc26..ccbc7ce 100644 --- a/soundcore-lib/src/lib.rs +++ b/soundcore-lib/src/lib.rs @@ -1,7 +1,11 @@ +pub mod api; pub mod base; pub mod devices; pub mod error; -mod statics; +mod models; +pub mod packets; +mod parsers; +pub mod statics; pub mod types; #[allow(non_snake_case)] mod utils; diff --git a/soundcore-lib/src/models.rs b/soundcore-lib/src/models.rs new file mode 100644 index 0000000..a115c81 --- /dev/null +++ b/soundcore-lib/src/models.rs @@ -0,0 +1,51 @@ +mod a3909_button_model; +mod age; +mod anc_mode; +mod auto_power; +mod battery; +mod button_model; +mod curr_sound_mode; +mod custom_anc; +mod eq; +mod eq_configuration; +mod eq_profile; +mod feature_flags; +mod fw; +mod game_mode; +mod gender; +mod hearid; +mod packet_header; +mod packet_kind; +mod serial; +mod side_tone; +mod sound_mode; +mod touch_tone; +mod trans_mode; +mod tws_status; +mod wear_detection; + +pub use a3909_button_model::*; +pub use age::*; +pub use anc_mode::*; +pub use auto_power::*; +pub use battery::*; +pub use button_model::*; +pub use curr_sound_mode::*; +pub use custom_anc::*; +pub use eq::*; +pub use eq_configuration::*; +pub use eq_profile::*; +pub use feature_flags::*; +pub use fw::*; +pub use game_mode::*; +pub use gender::*; +pub use hearid::*; +pub use packet_header::*; +pub use packet_kind::*; +pub use serial::*; +pub use side_tone::*; +pub use sound_mode::*; +pub use touch_tone::*; +pub use trans_mode::*; +pub use tws_status::*; +pub use wear_detection::*; diff --git a/soundcore-lib/src/models/a3909_button_model.rs b/soundcore-lib/src/models/a3909_button_model.rs new file mode 100644 index 0000000..efeab58 --- /dev/null +++ b/soundcore-lib/src/models/a3909_button_model.rs @@ -0,0 +1,131 @@ +use serde::{Deserialize, Serialize}; +use strum::FromRepr; + +/// This is the A3909 variant of the CustomBtnModel +/// TODO: Check if there are common models to other button models, if so move them to a common file +#[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "camelCase")] +pub struct A3909ButtonModel { + pub left: ButtonSide, + pub right: ButtonSide, +} +impl A3909ButtonModel { + fn bytes(&self) -> [u8; 12] { + let mut bytes = [0u8; 12]; + bytes[0..2].copy_from_slice(&self.left.double_press.bytes()); + bytes[2..4].copy_from_slice(&self.left.long_press.bytes()); + bytes[4..6].copy_from_slice(&self.right.double_press.bytes()); + bytes[6..8].copy_from_slice(&self.right.long_press.bytes()); + bytes[8..10].copy_from_slice(&self.left.single_press.bytes()); + bytes[10..12].copy_from_slice(&self.right.single_press.bytes()); + bytes + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "camelCase")] +pub struct ButtonSide { + pub double_press: TwsButtonAction, + pub single_press: NonTwsButtonAction, + pub long_press: TwsButtonAction, +} + +#[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "camelCase")] +pub struct TwsButtonAction { + pub non_tws_action: Action, /* Disconnected Action */ + pub tws_action: Action, /* Connected Action */ + pub enabled: bool, +} + +impl TwsButtonAction { + pub fn bytes(&self) -> [u8; 2] { + [ + self.enabled as u8, + (self.non_tws_action.as_u8() << 4) | (self.tws_action.as_u8() & 0x0F), + ] + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "camelCase")] +pub struct NonTwsButtonAction { + pub action: Action, + pub enabled: bool, +} + +impl NonTwsButtonAction { + pub fn bytes(&self) -> [u8; 2] { + [self.enabled as u8, self.action.as_u8() & 0x0F] + } +} + +#[derive( + Debug, Serialize, Deserialize, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, FromRepr, +)] +#[repr(u8)] +#[serde(rename_all = "camelCase")] +pub enum Action { + VolumeUp = 0, + VolumeDown = 1, + PreviousSong = 2, + NextSong = 3, + Trans = 4, /* TODO: Refered to as "custom_trans" in the source, unknown action */ + VoiceAssistant = 5, + PlayPause = 6, +} + +impl Action { + fn as_u8(&self) -> u8 { + *self as u8 + } +} + +#[cfg(test)] +mod a3909_button_model { + use super::*; + + #[test] + fn to_bytes() { + let model = A3909ButtonModel { + left: ButtonSide { + double_press: TwsButtonAction { + non_tws_action: Action::NextSong, + tws_action: Action::PreviousSong, + enabled: true, + }, + single_press: NonTwsButtonAction { + action: Action::VoiceAssistant, + enabled: false, + }, + long_press: TwsButtonAction { + non_tws_action: Action::NextSong, + tws_action: Action::PreviousSong, + enabled: true, + }, + }, + right: ButtonSide { + double_press: TwsButtonAction { + non_tws_action: Action::NextSong, + tws_action: Action::PreviousSong, + enabled: true, + }, + single_press: NonTwsButtonAction { + action: Action::VoiceAssistant, + enabled: false, + }, + long_press: TwsButtonAction { + non_tws_action: Action::NextSong, + tws_action: Action::PreviousSong, + enabled: true, + }, + }, + }; + + let expected = [ + 0x01, 0x32, 0x01, 0x32, 0x01, 0x32, 0x01, 0x32, 0x00, 0x05, 0x00, 0x05, + ]; + + assert_eq!(expected, model.bytes()); + } +} diff --git a/soundcore-lib/src/models/age.rs b/soundcore-lib/src/models/age.rs new file mode 100644 index 0000000..fe8ca9e --- /dev/null +++ b/soundcore-lib/src/models/age.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Default, Hash, +)] +pub struct AgeRange(u8); + +impl AgeRange { + pub fn from_u8(value: u8) -> Self { + Self(value) + } + + pub fn as_u8(&self) -> u8 { + self.0 + } +} diff --git a/soundcore-lib/src/models/anc_mode.rs b/soundcore-lib/src/models/anc_mode.rs new file mode 100644 index 0000000..e62291c --- /dev/null +++ b/soundcore-lib/src/models/anc_mode.rs @@ -0,0 +1,63 @@ +use serde::{Deserialize, Serialize}; +use strum::{Display, FromRepr}; + +#[repr(u8)] +#[derive( + Debug, + Serialize, + Deserialize, + Eq, + PartialEq, + Ord, + PartialOrd, + Clone, + Copy, + Default, + FromRepr, + Display, + Hash, +)] +#[serde(rename_all = "camelCase")] +pub enum ANCMode { + #[default] + Transport = 0, + Outdoor = 1, + Indoor = 2, + Custom = 3, +} + +impl ANCMode { + pub fn as_u8(&self) -> u8 { + *self as u8 + } + + pub fn from_u8(value: u8) -> Option { + Self::from_repr(value) + } +} + +#[cfg(test)] +mod anc_mode_tests { + use super::*; + + #[test] + fn init_from_u8() { + assert_eq!(ANCMode::from_u8(0), Some(ANCMode::Transport)); + assert_eq!(ANCMode::from_u8(1), Some(ANCMode::Outdoor)); + assert_eq!(ANCMode::from_u8(2), Some(ANCMode::Indoor)); + assert_eq!(ANCMode::from_u8(3), Some(ANCMode::Custom)); + } + + #[test] + fn init_from_u8_invalid() { + assert_eq!(ANCMode::from_u8(10), None); + } + + #[test] + fn returns_value() { + assert_eq!(ANCMode::Transport.as_u8(), 0); + assert_eq!(ANCMode::Outdoor.as_u8(), 1); + assert_eq!(ANCMode::Indoor.as_u8(), 2); + assert_eq!(ANCMode::Custom.as_u8(), 3); + } +} diff --git a/soundcore-lib/src/models/auto_power.rs b/soundcore-lib/src/models/auto_power.rs new file mode 100644 index 0000000..afda3d6 --- /dev/null +++ b/soundcore-lib/src/models/auto_power.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)] +#[serde(rename_all = "camelCase", tag = "type")] +pub struct AutoPowerOffOn { + pub enabled: bool, + pub index: u8, //TODO: Search for possible values and map to enum +} diff --git a/soundcore-lib/src/models/battery.rs b/soundcore-lib/src/models/battery.rs new file mode 100644 index 0000000..d73e371 --- /dev/null +++ b/soundcore-lib/src/models/battery.rs @@ -0,0 +1,49 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)] +#[serde(rename_all = "camelCase", tag = "type")] +pub enum Battery { + Single(SingleBattery), + Dual(DualBattery), +} + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)] +#[serde(rename_all = "camelCase", tag = "type")] +pub struct DualBattery { + pub left: SingleBattery, + pub right: SingleBattery, +} + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)] +#[serde(rename_all = "camelCase", tag = "type")] +pub struct SingleBattery { + pub charging: bool, + pub level: u8, +} + +impl From for Battery { + fn from(b: SingleBattery) -> Self { + Battery::Single(b) + } +} + +impl From for Battery { + fn from(b: DualBattery) -> Self { + Battery::Dual(b) + } +} + +impl Default for SingleBattery { + fn default() -> Self { + SingleBattery { + charging: false, + level: u8::MAX, + } + } +} + +impl Default for Battery { + fn default() -> Self { + Battery::Single(SingleBattery::default()) + } +} diff --git a/soundcore-lib/src/models/button_model.rs b/soundcore-lib/src/models/button_model.rs new file mode 100644 index 0000000..e60fad9 --- /dev/null +++ b/soundcore-lib/src/models/button_model.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +use super::A3909ButtonModel; + +#[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum ButtonModel { + A3909(A3909ButtonModel), +} diff --git a/soundcore-lib/src/models/curr_sound_mode.rs b/soundcore-lib/src/models/curr_sound_mode.rs new file mode 100644 index 0000000..616b9d6 --- /dev/null +++ b/soundcore-lib/src/models/curr_sound_mode.rs @@ -0,0 +1,63 @@ +use serde::{Deserialize, Serialize}; +use strum::{Display, FromRepr}; + +#[repr(u8)] +#[derive( + Debug, + Serialize, + Deserialize, + Eq, + PartialEq, + Ord, + PartialOrd, + Clone, + Copy, + Default, + FromRepr, + Display, + Hash, +)] +#[serde(rename_all = "camelCase")] +pub enum CurrentSoundMode { + ANC = 0, + Transparency = 1, + #[default] + Normal = 2, +} + +impl CurrentSoundMode { + pub fn as_u8(&self) -> u8 { + *self as u8 + } + + pub fn from_u8(value: u8) -> Option { + Self::from_repr(value) + } +} + +#[cfg(test)] +mod anc_mode_tests { + use super::*; + + #[test] + fn init_from_u8() { + assert_eq!(CurrentSoundMode::from_u8(0), Some(CurrentSoundMode::ANC)); + assert_eq!( + CurrentSoundMode::from_u8(1), + Some(CurrentSoundMode::Transparency) + ); + assert_eq!(CurrentSoundMode::from_u8(2), Some(CurrentSoundMode::Normal)); + } + + #[test] + fn init_from_u8_invalid() { + assert_eq!(CurrentSoundMode::from_u8(10), None); + } + + #[test] + fn returns_value() { + assert_eq!(CurrentSoundMode::ANC.as_u8(), 0); + assert_eq!(CurrentSoundMode::Transparency.as_u8(), 1); + assert_eq!(CurrentSoundMode::Normal.as_u8(), 2); + } +} diff --git a/soundcore-lib/src/models/custom_anc.rs b/soundcore-lib/src/models/custom_anc.rs new file mode 100644 index 0000000..0b380ce --- /dev/null +++ b/soundcore-lib/src/models/custom_anc.rs @@ -0,0 +1,19 @@ +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Default, Hash, +)] +pub struct CustomANC(u8); + +impl CustomANC { + pub fn from_u8(value: u8) -> Self { + match value { + 255 => CustomANC(255), + _ => CustomANC(value.clamp(0, 10)), + } + } + + pub fn as_u8(&self) -> u8 { + self.0 + } +} diff --git a/soundcore-lib/src/models/eq.rs b/soundcore-lib/src/models/eq.rs new file mode 100644 index 0000000..f916c0e --- /dev/null +++ b/soundcore-lib/src/models/eq.rs @@ -0,0 +1,71 @@ +use std::usize; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Default, Hash)] +pub struct StereoEQ { + pub left: MonoEQ, + pub right: MonoEQ, +} + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Default, Hash)] +pub struct MonoEQ { + pub values: [i8; 8], +} + +impl MonoEQ { + pub const MIN: i8 = -120; + pub const MAX: i8 = 120; + pub const MIN_BYTE: u8 = 0; + pub const MAX_BYTE: u8 = 240; + pub const MAX_FLOAT: f32 = 18.0; + pub const MIN_FLOAT: f32 = 6.0; + + pub fn new(values: [i8; 8]) -> Self { + Self { + values: values.map(|v| v.clamp(Self::MIN, Self::MAX)), + } + } + + pub fn from_bytes(bytes: &[u8; 8]) -> Self { + Self { + values: bytes.map(Self::from_byte), + } + } + + pub fn from_floats(floats: &[f32; 8]) -> Self { + Self { + values: floats.map(Self::from_float), + } + } + + pub fn to_bytes(&self) -> [u8; 8] { + self.values.map(Self::to_byte) + } + + pub fn to_floats(&self) -> [f32; 8] { + self.values.map(Self::to_float) + } + + /* Transforms the byte to the applicable u8 range */ + fn to_byte(value: i8) -> u8 { + value + .clamp(Self::MIN, Self::MAX) + .wrapping_add(Self::MIN.abs()) as u8 + } + + /* Transforms the byte to the applicable i8 range */ + fn from_byte(value: u8) -> i8 { + value + .clamp(Self::MIN_BYTE, Self::MAX_BYTE) + .wrapping_sub(Self::MIN.unsigned_abs()) as i8 + } + + fn to_float(_value: i8) -> f32 { + todo!() + } + + fn from_float(_value: f32) -> i8 { + todo!() + } +} diff --git a/soundcore-lib/src/models/eq_configuration.rs b/soundcore-lib/src/models/eq_configuration.rs new file mode 100644 index 0000000..93994a4 --- /dev/null +++ b/soundcore-lib/src/models/eq_configuration.rs @@ -0,0 +1,20 @@ +use super::{EQProfile, MonoEQ, StereoEQ}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)] +pub enum EQConfiguration { + Stereo(StereoEQConfiguration), + Mono(MonoEQConfiguration), +} + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)] +pub struct StereoEQConfiguration { + pub eq: StereoEQ, + pub profile: EQProfile, +} + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)] +pub struct MonoEQConfiguration { + pub eq: MonoEQ, + pub profile: EQProfile, +} diff --git a/soundcore-lib/src/models/eq_profile.rs b/soundcore-lib/src/models/eq_profile.rs new file mode 100644 index 0000000..5e13ba1 --- /dev/null +++ b/soundcore-lib/src/models/eq_profile.rs @@ -0,0 +1,86 @@ +use serde::{Deserialize, Serialize}; +use strum::{Display, EnumIter, EnumString, FromRepr}; + +use super::MonoEQ; + +#[repr(u16)] +#[derive( + FromRepr, + Debug, + Clone, + Copy, + Ord, + PartialOrd, + Eq, + PartialEq, + Hash, + Display, + EnumIter, + Serialize, + Deserialize, + EnumString, +)] +pub enum EQProfile { + SoundcoreSignature = 0x0000, + Acoustic = 0x0001, + BassBooster = 0x0002, + BassReducer = 0x0003, + Classical = 0x0004, + Podcast = 0x0005, + Dance = 0x0006, + Deep = 0x0007, + Electronic = 0x0008, + Flat = 0x0009, + HipHop = 0x000A, + Jazz = 0x000B, + Latin = 0x000C, + Lounge = 0x000D, + Piano = 0x000E, + Pop = 0x000F, + RnB = 0x0010, + Rock = 0x0011, + SmallSpeakers = 0x0012, + SpokenWord = 0x0013, + TrebleBooster = 0x0014, + TrebleReducer = 0x0015, + Custom = 0xFEFE, +} + +impl EQProfile { + pub fn id(&self) -> u16 { + *self as u16 + } + + pub fn from_id(id: u16) -> Option { + Self::from_repr(id) + } + + pub fn eq(&self) -> MonoEQ { + let eq: [i8; 8] = match self { + Self::SoundcoreSignature => [0, 0, 0, 0, 0, 0, 0, 0], + Self::Acoustic => [40, 10, 20, 20, 40, 40, 40, 20], + Self::BassBooster => [40, 30, 10, 0, 0, 0, 0, 0], + Self::BassReducer => [-40, -30, -10, 0, 0, 0, 0, 0], + Self::Classical => [30, 30, -20, -20, 0, 20, 30, 40], + Self::Podcast => [-30, 20, 40, 40, 30, 20, 0, -20], + Self::Dance => [20, -30, -10, 10, 20, 20, 10, -30], + Self::Deep => [20, 10, 30, 30, 20, -20, -40, -50], + Self::Electronic => [30, 20, -20, 20, 10, 20, 30, 30], + Self::Flat => [-20, -20, -10, 0, 0, 0, -20, -20], + Self::HipHop => [20, 30, -10, -10, 20, -10, 20, 30], + Self::Jazz => [20, 20, -20, -20, 0, 20, 30, 40], + Self::Latin => [0, 0, -20, -20, -20, 0, 30, 50], + Self::Lounge => [-10, 20, 40, 30, 0, -20, 20, 10], + Self::Piano => [0, 30, 30, 20, 40, 50, 30, 40], + Self::Pop => [-10, 10, 30, 30, 10, -10, -20, -30], + Self::RnB => [60, 20, -20, -20, 20, 30, 30, 40], + Self::Rock => [30, 20, -10, -10, 10, 30, 30, 30], + Self::SmallSpeakers => [40, 30, 10, 0, -20, -30, -40, -40], + Self::SpokenWord => [-30, -20, 10, 20, 20, 10, 0, -30], + Self::TrebleBooster => [-20, -20, -20, -10, 10, 20, 20, 40], + Self::TrebleReducer => [0, 0, 0, -20, -30, -40, -40, -60], + _ => [0, 0, 0, 0, 0, 0, 0, 0], + }; + MonoEQ::new(eq) + } +} diff --git a/soundcore-lib/src/models/feature_flags.rs b/soundcore-lib/src/models/feature_flags.rs new file mode 100644 index 0000000..ebff849 --- /dev/null +++ b/soundcore-lib/src/models/feature_flags.rs @@ -0,0 +1,25 @@ +#![allow(non_camel_case_types)] +use enumflags2::bitflags; +use serde::{Deserialize, Serialize}; +use strum::EnumIter; + +#[derive( + Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash, EnumIter, +)] +#[bitflags] +#[repr(u16)] +pub enum SoundcoreFeatureFlags { + SOUND_MODE, + ANC_MODE, + TRANS_MODE, + CUSTOM_ANC, + EQ, + STEREO_EQ, + DRC, + HEARID, + WEAR_DETECTION, + CUSTOM_BUTTONS, + TOUCH_TONE, + GAME_MODE, + AUTO_POWER_OFF_ON, +} diff --git a/soundcore-lib/src/models/fw.rs b/soundcore-lib/src/models/fw.rs new file mode 100644 index 0000000..66e6fdf --- /dev/null +++ b/soundcore-lib/src/models/fw.rs @@ -0,0 +1,57 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)] +// TODO: Create parser +pub enum DeviceFirmware { + DUAL(FirmwareVer, FirmwareVer), + SINGLE(FirmwareVer), +} + +#[derive( + Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Default, Hash, +)] +#[serde(rename_all = "camelCase", tag = "type")] +pub struct FirmwareVer { + major: u8, + minor: u8, +} + +impl FirmwareVer { + pub fn new(major: u8, minor: u8) -> Self { + Self { major, minor } + } + + pub fn major(&self) -> u8 { + self.major + } + + pub fn minor(&self) -> u8 { + self.minor + } +} + +impl ToString for FirmwareVer { + fn to_string(&self) -> String { + format!("{:02}.{:02}", self.major, self.minor) + } +} + +#[cfg(test)] +mod fw_tests { + use super::*; + + #[test] + fn simple_init() { + let fw = FirmwareVer::new(1, 2); + assert_eq!(fw.major(), 1); + assert_eq!(fw.minor(), 2); + assert_eq!(fw.to_string(), "01.02"); + } + + #[test] + fn comparison() { + let new = FirmwareVer::new(1, 22); + let old = FirmwareVer::new(1, 21); + assert!(new > old); + } +} diff --git a/soundcore-lib/src/models/game_mode.rs b/soundcore-lib/src/models/game_mode.rs new file mode 100644 index 0000000..e6afb07 --- /dev/null +++ b/soundcore-lib/src/models/game_mode.rs @@ -0,0 +1,6 @@ +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Default, Hash, +)] +pub struct GameMode(pub bool); diff --git a/soundcore-lib/src/models/gender.rs b/soundcore-lib/src/models/gender.rs new file mode 100644 index 0000000..adcc30d --- /dev/null +++ b/soundcore-lib/src/models/gender.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Default, Hash, +)] +pub struct Gender(u8); + +impl Gender { + pub fn from_u8(value: u8) -> Self { + Self(value) + } + + pub fn as_u8(&self) -> u8 { + self.0 + } +} diff --git a/soundcore-lib/src/models/hearid.rs b/soundcore-lib/src/models/hearid.rs new file mode 100644 index 0000000..13153ae --- /dev/null +++ b/soundcore-lib/src/models/hearid.rs @@ -0,0 +1,30 @@ +use serde::{Deserialize, Serialize}; + +use super::StereoEQ; + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)] +pub struct HearIDType(pub u8); // TODO: Move to enum? + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)] +pub struct HearIDMusicType(pub u8); // TODO: Move to enum? + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)] +pub enum HearID { + Base(BaseHearID), + Custom(CustomHearID), +} + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)] +pub struct BaseHearID { + pub enabled: bool, + pub values: StereoEQ, + pub time: i32, +} + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)] +pub struct CustomHearID { + pub base: BaseHearID, + pub hearid_type: HearIDType, + pub hearid_music_type: HearIDMusicType, + pub custom_values: Option, +} diff --git a/soundcore-lib/src/models/packet_header.rs b/soundcore-lib/src/models/packet_header.rs new file mode 100644 index 0000000..32febc7 --- /dev/null +++ b/soundcore-lib/src/models/packet_header.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +use super::ResponsePacketKind; + +#[derive(Debug, Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct ResponsePacketHeader { + pub kind: ResponsePacketKind, + pub length: u16, +} diff --git a/soundcore-lib/src/models/packet_kind.rs b/soundcore-lib/src/models/packet_kind.rs new file mode 100644 index 0000000..50e85c1 --- /dev/null +++ b/soundcore-lib/src/models/packet_kind.rs @@ -0,0 +1,18 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[serde(rename_all = "camelCase")] +pub enum ResponsePacketKind { + /* Packets which *should* update the state in some way */ + StateUpdate, + InfoUpdate, + SoundModeUpdate, + BattLevelUpdate, + BattChargingUpdate, + LDACUpdate, + /* Acknowledgment packets */ + SetSoundModeAck, + SetEqAck, + SetEqDrcAck, + Unknown, +} diff --git a/soundcore-lib/src/models/serial.rs b/soundcore-lib/src/models/serial.rs new file mode 100644 index 0000000..6cb2834 --- /dev/null +++ b/soundcore-lib/src/models/serial.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)] +pub struct SerialNumber(pub Arc); + +impl SerialNumber { + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl Default for SerialNumber { + fn default() -> Self { + Self("XXXXXXXXXXXXXXXX".into()) + } +} + +impl From<&str> for SerialNumber { + fn from(s: &str) -> Self { + SerialNumber(s.into()) + } +} diff --git a/soundcore-lib/src/models/side_tone.rs b/soundcore-lib/src/models/side_tone.rs new file mode 100644 index 0000000..24d29af --- /dev/null +++ b/soundcore-lib/src/models/side_tone.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Default, Hash, +)] +pub struct SideTone(pub bool); + +impl From for SideTone { + fn from(value: bool) -> Self { + Self(value) + } +} diff --git a/soundcore-lib/src/models/sound_mode.rs b/soundcore-lib/src/models/sound_mode.rs new file mode 100644 index 0000000..242dfb3 --- /dev/null +++ b/soundcore-lib/src/models/sound_mode.rs @@ -0,0 +1,25 @@ +use serde::{Deserialize, Serialize}; + +use super::{ANCMode, CurrentSoundMode, CustomANC, TransparencyMode}; + +#[derive( + Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Default, Hash, +)] +#[serde(rename_all = "camelCase", tag = "type")] +pub struct SoundMode { + pub current: CurrentSoundMode, + pub anc_mode: ANCMode, + pub trans_mode: TransparencyMode, + pub custom_anc: CustomANC, +} + +impl SoundMode { + pub fn to_bytes(&self) -> [u8; 4] { + [ + self.current.as_u8(), + self.anc_mode.as_u8(), + self.trans_mode.as_u8(), + self.custom_anc.as_u8(), + ] + } +} diff --git a/soundcore-lib/src/models/touch_tone.rs b/soundcore-lib/src/models/touch_tone.rs new file mode 100644 index 0000000..03c6633 --- /dev/null +++ b/soundcore-lib/src/models/touch_tone.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Default, Hash, +)] +pub struct TouchTone(pub bool); + +impl From for TouchTone { + fn from(b: bool) -> Self { + TouchTone(b) + } +} diff --git a/soundcore-lib/src/models/trans_mode.rs b/soundcore-lib/src/models/trans_mode.rs new file mode 100644 index 0000000..27f3c49 --- /dev/null +++ b/soundcore-lib/src/models/trans_mode.rs @@ -0,0 +1,60 @@ +use serde::{Deserialize, Serialize}; +use strum::{Display, FromRepr}; + +#[repr(u8)] +#[derive( + Debug, + Serialize, + Deserialize, + Eq, + PartialEq, + Ord, + PartialOrd, + Clone, + Copy, + Default, + FromRepr, + Display, + Hash, +)] +#[serde(rename_all = "camelCase")] +pub enum TransparencyMode { + #[default] + FullyTransparent = 0, + Vocal = 1, +} + +impl TransparencyMode { + pub fn as_u8(&self) -> u8 { + *self as u8 + } + + pub fn from_u8(value: u8) -> Option { + Self::from_repr(value) + } +} + +#[cfg(test)] +mod anc_mode_tests { + use super::*; + + #[test] + fn init_from_u8() { + assert_eq!( + TransparencyMode::from_u8(0), + Some(TransparencyMode::FullyTransparent) + ); + assert_eq!(TransparencyMode::from_u8(1), Some(TransparencyMode::Vocal)); + } + + #[test] + fn init_from_u8_invalid() { + assert_eq!(TransparencyMode::from_u8(10), None); + } + + #[test] + fn returns_value() { + assert_eq!(TransparencyMode::FullyTransparent.as_u8(), 0); + assert_eq!(TransparencyMode::Vocal.as_u8(), 1); + } +} diff --git a/soundcore-lib/src/models/tws_status.rs b/soundcore-lib/src/models/tws_status.rs new file mode 100644 index 0000000..ea739bf --- /dev/null +++ b/soundcore-lib/src/models/tws_status.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Default, Hash, +)] +pub struct TwsStatus(pub bool); + +impl From for TwsStatus { + fn from(value: bool) -> Self { + Self(value) + } +} diff --git a/soundcore-lib/src/models/wear_detection.rs b/soundcore-lib/src/models/wear_detection.rs new file mode 100644 index 0000000..0a0db3c --- /dev/null +++ b/soundcore-lib/src/models/wear_detection.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Default, Hash, +)] +pub struct WearDetection(pub bool); + +impl From for WearDetection { + fn from(b: bool) -> Self { + WearDetection(b) + } +} diff --git a/soundcore-lib/src/packets.rs b/soundcore-lib/src/packets.rs new file mode 100644 index 0000000..1d28ef9 --- /dev/null +++ b/soundcore-lib/src/packets.rs @@ -0,0 +1,3 @@ +mod response; + +pub use response::*; diff --git a/soundcore-lib/src/packets/response.rs b/soundcore-lib/src/packets/response.rs new file mode 100644 index 0000000..5883efd --- /dev/null +++ b/soundcore-lib/src/packets/response.rs @@ -0,0 +1,30 @@ +use crate::{ + models::ResponsePacketKind, + parsers::{parse_and_check_checksum, parse_packet_header}, +}; + +use nom::error::{VerboseError}; + +pub enum ResponsePacket { + DeviceState(DeviceStateResponse), + DeviceInfo, // TODO +} + +impl ResponsePacket { + pub fn from_bytes(bytes: &[u8]) -> Result>> { + let bytes = parse_and_check_checksum(bytes)?.0; + let (bytes, packet_header) = parse_packet_header(bytes)?; + Ok(match packet_header.kind { + ResponsePacketKind::StateUpdate => { + Self::DeviceState(parse_state_update_packet(bytes)?.1) + } + _ => unimplemented!(), + }) + } +} + +mod info; +mod state; + +pub use info::*; +pub use state::*; diff --git a/soundcore-lib/src/packets/response/info.rs b/soundcore-lib/src/packets/response/info.rs new file mode 100644 index 0000000..ce79c6a --- /dev/null +++ b/soundcore-lib/src/packets/response/info.rs @@ -0,0 +1,12 @@ +mod a3951; + +pub use a3951::*; +use serde::{Deserialize, Serialize}; + +use crate::models::{DeviceFirmware, SerialNumber}; + +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Hash, Default)] +pub struct DeviceInfoResponse { + pub sn: Option, + pub fw: Option, +} diff --git a/soundcore-lib/src/packets/response/info/a3951.rs b/soundcore-lib/src/packets/response/info/a3951.rs new file mode 100644 index 0000000..e69de29 diff --git a/soundcore-lib/src/packets/response/state.rs b/soundcore-lib/src/packets/response/state.rs new file mode 100644 index 0000000..2ce8095 --- /dev/null +++ b/soundcore-lib/src/packets/response/state.rs @@ -0,0 +1,44 @@ +use enumflags2::BitFlags; +use nom::{combinator::map, error::context}; +use serde::{Deserialize, Serialize}; + +use crate::{ + models::{ + AgeRange, Battery, ButtonModel, CustomHearID, SideTone, + SoundMode, SoundcoreFeatureFlags, TwsStatus, WearDetection, + }, + parsers::{SoundcoreParseError, SoundcoreParseResult}, +}; + +/// This is a generalized version of the state responses for all devices +/// All device-specific state responses should be able to be converted to this type +/// Also, this must be impl Into +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Hash, Default)] +pub struct DeviceStateResponse { + pub feature_flags: BitFlags, + pub battery: Battery, + pub sound_mode: SoundMode, + pub host_device: Option, + pub tws_status: Option, + pub button_model: Option, + pub side_tone: Option, + pub hearid_eq_preset: Option, + pub wear_detection: Option, + pub hear_id: Option, + pub age_range: Option, +} + +// TODO +pub fn parse_state_update_packet<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_state_update", |bytes| { + map( + parse_a3951_state_response::<'a, E>, + DeviceStateResponse::from, + )(bytes) + })(bytes) +} + +mod a3951; +pub use a3951::*; diff --git a/soundcore-lib/src/packets/response/state/a3951.rs b/soundcore-lib/src/packets/response/state/a3951.rs new file mode 100644 index 0000000..79f6c96 --- /dev/null +++ b/soundcore-lib/src/packets/response/state/a3951.rs @@ -0,0 +1,191 @@ +use enumflags2::{make_bitflags, BitFlags}; +use nom::{ + combinator::{all_consuming, opt}, + error::context, + number::complete::{le_u16, le_u8}, + sequence::tuple, +}; +use serde::{Deserialize, Serialize}; + +use crate::models::{ + A3909ButtonModel, AgeRange, Battery, ButtonModel, CustomHearID, DualBattery, Gender, SideTone, + SoundMode, SoundcoreFeatureFlags, StereoEQConfiguration, TouchTone, TwsStatus, WearDetection, +}; + +use crate::parsers::{ + bool_parser, parse_a3909_button_model, parse_age_range, parse_custom_hear_id, + parse_dual_battery, parse_gender, parse_sound_mode, parse_stereo_eq_configuration, + SoundcoreParseError, SoundcoreParseResult, +}; + +use super::DeviceStateResponse; + +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] +pub struct A3951StateResponse { + pub host_device: u8, + pub tws_status: TwsStatus, + pub battery: DualBattery, + pub eq: StereoEQConfiguration, + pub gender: Gender, + pub age_range: AgeRange, + pub hear_id: CustomHearID, + pub button_model: A3909ButtonModel, + pub sound_mode: SoundMode, + pub side_tone: SideTone, + pub wear_detection: WearDetection, + pub touch_tone: TouchTone, + pub hearid_eq_preset: Option, + pub new_battery: Option<(u8, u8)>, +} + +const A3951_FEATURE_FLAGS: BitFlags = make_bitflags!(SoundcoreFeatureFlags::{ + SOUND_MODE + | ANC_MODE + | TRANS_MODE + | CUSTOM_ANC + | CUSTOM_BUTTONS + | WEAR_DETECTION + | EQ + | STEREO_EQ + | DRC + | HEARID + | TOUCH_TONE +}); + +impl From for DeviceStateResponse { + fn from(value: A3951StateResponse) -> Self { + DeviceStateResponse { + feature_flags: A3951_FEATURE_FLAGS, + battery: Battery::Dual(value.battery), + sound_mode: value.sound_mode, + host_device: Some(value.host_device), + tws_status: Some(value.tws_status), + button_model: Some(ButtonModel::A3909(value.button_model)), + side_tone: Some(value.side_tone), + hearid_eq_preset: value.hearid_eq_preset, + wear_detection: Some(value.wear_detection), + hear_id: Some(value.hear_id), + age_range: Some(value.age_range), + } + } +} + +pub fn parse_a3951_state_response<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "a3951_state_response", + all_consuming(|bytes| { + let ( + bytes, + ( + host_device, + tws_status, + battery, + eq, + gender, + age_range, + hear_id, + button_model, + sound_mode, + side_tone, + wear_detection, + touch_tone, + ), + ) = tuple(( + le_u8, + bool_parser::, + parse_dual_battery, + parse_stereo_eq_configuration, + parse_gender, + parse_age_range, + parse_custom_hear_id, + parse_a3909_button_model, + parse_sound_mode, + bool_parser::, + bool_parser::, + bool_parser::, + ))(bytes)?; + + // Optional Fields + let (bytes, hearid_eq_preset) = opt(le_u16)(bytes)?; + let (bytes, new_battery) = opt(tuple((le_u8, le_u8)))(bytes)?; + + Ok(( + bytes, + A3951StateResponse { + host_device, + tws_status, + battery, + eq, + gender, + age_range, + hear_id, + button_model, + sound_mode, + side_tone, + wear_detection, + touch_tone, + hearid_eq_preset, + new_battery, + }, + )) + }), + )(bytes) +} + +#[cfg(test)] +mod a3951_state { + + use super::*; + + const RESP_BYTES: [u8; 86] = [ + 1, 1, 5, 5, 1, 0, 254, 254, 160, 150, 130, 120, 120, 120, 120, 120, 160, 150, 130, 120, + 120, 120, 120, 120, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 1, 99, 1, 84, 1, 102, 1, 84, 0, 1, 0, 0, 0, 1, 1, 6, 0, + 1, 0, 0, 0, + ]; + const ORIG_RESP_BYTES: [u8; 97] = [ + 9, 255, 0, 0, 1, 1, 1, 97, 0, 1, 1, 5, 5, 1, 0, 254, 254, 160, 150, 130, 120, 120, 120, + 120, 120, 160, 150, 130, 120, 120, 120, 120, 120, 255, 255, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 99, 1, 84, 1, 102, 1, + 84, 0, 1, 0, 0, 0, 1, 1, 6, 0, 1, 0, 0, 0, 0, 242, + ]; + + #[test] + fn should_parse_state_same_as_old() { + let (remaining, state) = + parse_a3951_state_response::>(&RESP_BYTES).unwrap(); + let old_state = crate::devices::A3951::device_status(&ORIG_RESP_BYTES); + + assert!(remaining.is_empty()); + + assert_eq!(state.host_device, old_state.host_device); + assert_eq!(state.tws_status.0, old_state.tws_status); + assert_eq!(state.battery.left.charging, old_state.battery_charging.left); + assert_eq!(state.battery.left.level, old_state.battery_level.left); + assert_eq!( + state.battery.right.charging, + old_state.battery_charging.right + ); + assert_eq!(state.battery.right.level, old_state.battery_level.right); + assert_eq!( + state.sound_mode.anc_mode.as_u8(), + old_state.anc_status.anc_option + ); + assert_eq!( + state.sound_mode.custom_anc.as_u8(), + old_state.anc_status.anc_custom + ); + assert_eq!( + state.sound_mode.anc_mode.as_u8(), + old_state.anc_status.anc_option + ); + assert_eq!( + state.sound_mode.trans_mode.as_u8(), + old_state.anc_status.transparency_option + ); + } +} diff --git a/soundcore-lib/src/parsers.rs b/soundcore-lib/src/parsers.rs new file mode 100644 index 0000000..c5c35fb --- /dev/null +++ b/soundcore-lib/src/parsers.rs @@ -0,0 +1,45 @@ +use nom::{ + error::{ContextError, ParseError}, + IResult, +}; + +pub use a3909_button_model::*; +pub use age::*; +pub use auto_power::*; +pub use base::*; +pub use battery::*; +pub use checksum::*; +pub use eq::*; +pub use eq_configuration::*; +pub use game_mode::*; +pub use gender::*; +pub use hearid::*; +pub use packet_header::*; +pub use serial::*; +pub use sound_mode::*; +pub use touch_tone::*; +pub use wear_detection::*; + +mod a3909_button_model; +mod age; +mod auto_power; +mod base; +mod battery; +mod checksum; +mod eq; +mod eq_configuration; +mod game_mode; +mod gender; +mod hearid; +mod packet_header; +mod serial; +mod side_tone; +mod sound_mode; +mod touch_tone; +mod wear_detection; + +pub type SoundcoreParseResult<'a, T, E> = IResult<&'a [u8], T, E>; + +pub trait SoundcoreParseError<'a>: ParseError<&'a [u8]> + ContextError<&'a [u8]> {} + +impl<'a> SoundcoreParseError<'a> for nom::error::VerboseError<&'a [u8]> {} diff --git a/soundcore-lib/src/parsers/a3909_button_model.rs b/soundcore-lib/src/parsers/a3909_button_model.rs new file mode 100644 index 0000000..fc0fb3c --- /dev/null +++ b/soundcore-lib/src/parsers/a3909_button_model.rs @@ -0,0 +1,116 @@ +use nom::{ + combinator::{map, map_opt}, + error::context, + number::complete::le_u8, + sequence::{pair, tuple}, +}; + +use crate::models::{A3909ButtonModel, Action, ButtonSide, NonTwsButtonAction, TwsButtonAction}; + +use super::{base::parse_bool, SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_a3909_button_model<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_a3909_button_model", + map( + tuple(( + parse_tws_button, + parse_tws_button, + parse_tws_button, + parse_tws_button, + parse_non_tws_button, + parse_non_tws_button, + )), + |( + l_double_press, + l_long_press, + r_double_press, + r_long_press, + l_single_press, + r_single_press, + )| { + A3909ButtonModel { + left: ButtonSide { + double_press: l_double_press, + long_press: l_long_press, + single_press: l_single_press, + }, + right: ButtonSide { + double_press: r_double_press, + long_press: r_long_press, + single_press: r_single_press, + }, + } + }, + ), + )(bytes) +} + +fn parse_tws_button<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_a3909_tws_button", + map_opt(pair(parse_bool, le_u8), |(is_enabled, action)| { + Some(TwsButtonAction { + non_tws_action: Action::from_repr(action & 0x0F)?, + tws_action: Action::from_repr(action >> 4)?, + enabled: is_enabled, + }) + }), + )(bytes) +} + +fn parse_non_tws_button<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_a3909_non_tws_button", + map_opt(pair(parse_bool, le_u8), |(is_enabled, action)| { + Some(NonTwsButtonAction { + action: Action::from_repr(action)?, + enabled: is_enabled, + }) + }), + )(bytes) +} + +#[cfg(test)] +mod a3909_button_model { + use super::*; + + const BYTES: [u8; 12] = [1, 99, 1, 84, 1, 102, 1, 84, 0, 1, 0, 0]; + + #[test] + fn parse_valid_bytes() { + let model = parse_a3909_button_model::>(&BYTES); + assert!(model.is_ok()); + + let (remaining, model) = model.unwrap(); + assert!(remaining.is_empty()); + + assert_eq!(model.left.double_press.non_tws_action, Action::NextSong); + assert_eq!(model.left.double_press.tws_action, Action::PlayPause); + assert!(model.left.double_press.enabled); + + assert_eq!(model.left.single_press.action, Action::VolumeDown); + assert!(!model.left.single_press.enabled); + + assert_eq!(model.left.long_press.non_tws_action, Action::Trans); + assert_eq!(model.left.long_press.tws_action, Action::VoiceAssistant); + assert!(model.left.long_press.enabled); + + assert_eq!(model.right.double_press.non_tws_action, Action::PlayPause); + assert_eq!(model.right.double_press.tws_action, Action::PlayPause); + assert!(model.right.double_press.enabled); + + assert_eq!(model.right.single_press.action, Action::VolumeUp); + assert!(!model.right.single_press.enabled); + + assert_eq!(model.right.long_press.non_tws_action, Action::Trans); + assert_eq!(model.right.long_press.tws_action, Action::VoiceAssistant); + assert!(model.right.long_press.enabled); + } +} diff --git a/soundcore-lib/src/parsers/age.rs b/soundcore-lib/src/parsers/age.rs new file mode 100644 index 0000000..9b09f77 --- /dev/null +++ b/soundcore-lib/src/parsers/age.rs @@ -0,0 +1,11 @@ +use nom::{combinator::map, error::context, number::complete::le_u8}; + +use crate::models::AgeRange; + +use super::{SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_age_range<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_age_range", map(le_u8, AgeRange::from_u8))(bytes) +} diff --git a/soundcore-lib/src/parsers/auto_power.rs b/soundcore-lib/src/parsers/auto_power.rs new file mode 100644 index 0000000..1956c2c --- /dev/null +++ b/soundcore-lib/src/parsers/auto_power.rs @@ -0,0 +1,16 @@ +use nom::{combinator::map, error::context, number::complete::le_u8, sequence::tuple}; + +use crate::models::AutoPowerOffOn; + +use super::{parse_bool, SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_auto_power_off_on<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_auto_power_off_on", + map(tuple((parse_bool, le_u8)), |(enabled, index)| { + AutoPowerOffOn { enabled, index } + }), + )(bytes) +} diff --git a/soundcore-lib/src/parsers/base.rs b/soundcore-lib/src/parsers/base.rs new file mode 100644 index 0000000..f84223b --- /dev/null +++ b/soundcore-lib/src/parsers/base.rs @@ -0,0 +1,104 @@ +use nom::{ + bytes::complete::take, + combinator::{map, map_opt}, + error::{ParseError}, + multi::count, + number::complete::le_u8, +}; + +use super::{SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_bool<'a, E: ParseError<&'a [u8]>>(input: &'a [u8]) -> SoundcoreParseResult { + map(le_u8, |value| value == 1)(input) +} + +pub fn parse_str<'a, E>(len: usize) -> impl Fn(&'a [u8]) -> SoundcoreParseResult<&'a str, E> +where + E: SoundcoreParseError<'a>, +{ + move |input| map_opt(take(len), |bytes| std::str::from_utf8(bytes).ok())(input) +} + +pub fn bool_parser<'a, T, E>(bytes: &'a [u8]) -> SoundcoreParseResult +where + T: From, + E: SoundcoreParseError<'a>, +{ + map(parse_bool, T::from)(bytes) +} + +pub fn u8_parser<'a, T, E>(bytes: &'a [u8]) -> SoundcoreParseResult +where + T: From, + E: SoundcoreParseError<'a>, +{ + map(le_u8, T::from)(bytes) +} + +pub fn take_last_byte<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + let (_arr, last_byte) = le_u8(&bytes[bytes.len() - 1..])?; + Ok((&bytes[..bytes.len() - 1], last_byte)) +} + +pub fn take_bytes_from_end<'a, E: SoundcoreParseError<'a>>( + size: usize, +) -> impl Fn(&'a [u8]) -> SoundcoreParseResult, E> { + move |bytes| { + let (_, mut end_bytes) = count(take_last_byte, size)(bytes)?; + end_bytes.reverse(); + Ok((&bytes[..bytes.len() - size], end_bytes)) + } +} + +#[cfg(test)] +mod base_parsers { + use super::*; + + #[test] + fn parses_str() { + let input = b"ExampleString"; + let expected = Ok((&b""[..], "ExampleString")); + let actual = parse_str::>(input.len())(input); + + assert_eq!(expected, actual); + } + + #[test] + fn parses_true_bool() { + let input = b"\x01"; + let expected = Ok((&b""[..], true)); + let actual = parse_bool::>(input); + + assert_eq!(expected, actual); + } + + #[test] + fn parses_false_bool() { + let input = b"\x00"; + let expected = Ok((&b""[..], false)); + let actual = parse_bool::>(input); + + assert_eq!(expected, actual); + } + + #[test] + fn parses_last_byte() { + let input = [0x00, 0x00, 0xFF, 0xEE]; + let expected: Result<(&[u8], u8), _> = Ok((&[0x00, 0x00, 0xFF], 0xEE)); + let actual = take_last_byte::>(&input); + + assert_eq!(expected, actual); + } + + #[test] + fn takes_bytes_from_end() { + let input = [0x00, 0x00, 0x10, 0x20, 0x30, 0xAA, 0xFF]; + let expected: Result<(&[u8], Vec), _> = + Ok((&[0x00, 0x00, 0x10, 0x20, 0x30], vec![0xAA, 0xFF])); + let actual = take_bytes_from_end::>(2)(&input); + + assert_eq!(expected, actual); + } +} diff --git a/soundcore-lib/src/parsers/battery.rs b/soundcore-lib/src/parsers/battery.rs new file mode 100644 index 0000000..a37aadf --- /dev/null +++ b/soundcore-lib/src/parsers/battery.rs @@ -0,0 +1,82 @@ +use nom::{combinator::map, error::context, number::complete::le_u8, sequence::tuple}; + +use crate::models::{DualBattery, SingleBattery}; + +use super::{base::parse_bool, SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_dual_battery<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_dual_batt", + map( + tuple((le_u8, le_u8, parse_bool, parse_bool)), + |(left_level, right_level, left_charging, right_charging)| DualBattery { + left: SingleBattery { + charging: left_charging, + level: left_level, + }, + right: { + SingleBattery { + charging: right_charging, + level: right_level, + } + }, + }, + ), + )(bytes) +} + +pub fn parse_single_battery<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_single_batt", + map(tuple((le_u8, parse_bool)), |(level, charging)| { + SingleBattery { charging, level } + }), + )(bytes) +} + +#[cfg(test)] +mod battery_parser { + use super::*; + + #[test] + fn parses_dual_battery() { + let bytes = [0x05, 0x01, 0x00, 0x01]; + let result = parse_dual_battery::>(&bytes); + assert!(result.is_ok()); + let (remaining, battery) = result.unwrap(); + assert_eq!(remaining, &b""[..]); + assert_eq!( + battery, + DualBattery { + left: SingleBattery { + charging: false, + level: 5 + }, + right: SingleBattery { + charging: true, + level: 1 + } + } + ); + } + + #[test] + fn parses_single_battery() { + let bytes = [0x05, 0x01]; + let result = parse_single_battery::>(&bytes); + assert!(result.is_ok()); + let (remaining, battery) = result.unwrap(); + assert_eq!(remaining, &b""[..]); + assert_eq!( + battery, + SingleBattery { + charging: true, + level: 5 + } + ); + } +} diff --git a/soundcore-lib/src/parsers/checksum.rs b/soundcore-lib/src/parsers/checksum.rs new file mode 100644 index 0000000..4f91a3c --- /dev/null +++ b/soundcore-lib/src/parsers/checksum.rs @@ -0,0 +1,52 @@ + + +use nom::{ + combinator::{map_opt}, + error::context, +}; + +use super::{take_bytes_from_end, SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_and_check_checksum<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult<(), E> { + context( + "parse_and_check_checksum", + map_opt(take_bytes_from_end(2usize), |last_bytes| { + let checksum = generate_checksum(&bytes[..bytes.len() - 1]); + match last_bytes == [0x00, checksum] { + // TODO: Padding bit?? + true => Some(()), + false => None, + } + }), + )(bytes) +} + +fn generate_checksum<'a, I>(bytes: I) -> u8 +where + I: IntoIterator, +{ + bytes.into_iter().fold(0, |acc, x| acc.wrapping_add(*x)) +} + +#[cfg(test)] +mod checksum_parser { + use super::*; + + #[test] + fn parses_and_checks_checksum() { + let input = [0x01, 0x02, 0x00, 0x00, 0x00, 0x03]; + let expected: (&[u8], ()) = (&[0x01, 0x02, 0x00, 0x00], ()); + let actual = parse_and_check_checksum::>(&input); + assert!(actual.is_ok()); + assert_eq!(expected, actual.unwrap()); + } + + #[test] + fn fails_to_parse_and_check_checksum() { + let input = [0x01, 0x02, 0x00, 0x00, 0x00, 0x05]; + let res = parse_and_check_checksum::>(&input); + assert!(res.is_err()); + } +} diff --git a/soundcore-lib/src/parsers/eq.rs b/soundcore-lib/src/parsers/eq.rs new file mode 100644 index 0000000..ce922d1 --- /dev/null +++ b/soundcore-lib/src/parsers/eq.rs @@ -0,0 +1,75 @@ +use nom::{ + bytes::complete::take, + combinator::{map}, + error::context, + sequence::tuple, +}; + +use crate::models::{MonoEQ, StereoEQ}; + +use super::{SoundcoreParseError, SoundcoreParseResult}; + +// The official app supports up to 10-Band EQs, +// since we currently support up to 8, +// we should use all consuming +pub fn parse_mono_eq<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_mono_eq", + map(take(8usize), |bytes: &[u8]| { + MonoEQ::from_bytes(bytes.try_into().unwrap()) + }), + )(bytes) +} + +pub fn parse_stereo_eq<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_stereo_eq", + map(tuple((parse_mono_eq, parse_mono_eq)), |(left, right)| { + StereoEQ { left, right } + }), + )(bytes) +} + +#[cfg(test)] +mod eq_parser { + use super::*; + + #[test] + fn parses_valid_mono_bytes() { + let bytes = [0x00, 0x10, 0xF0, 0x10, 0x00, 0x10, 0xF0, 0x10]; + let expected = Ok(( + &b""[..], + MonoEQ { + values: [-120, -104, 120, -104, -120, -104, 120, -104], + }, + )); + let output = parse_mono_eq::>(&bytes); + + assert_eq!(expected, output); + } + + #[test] + fn parses_valid_stereo_bytes() { + let bytes = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, + ]; + let eq = MonoEQ::new([120, 120, 120, 120, 120, 120, 120, 120]); + let expected = Ok(( + &b""[..], + StereoEQ { + left: MonoEQ { + values: [-120, -120, -120, -120, -120, -120, -120, -120], + }, + right: eq, + }, + )); + let output = parse_stereo_eq::>(&bytes); + + assert_eq!(expected, output); + } +} diff --git a/soundcore-lib/src/parsers/eq_configuration.rs b/soundcore-lib/src/parsers/eq_configuration.rs new file mode 100644 index 0000000..d3daf0e --- /dev/null +++ b/soundcore-lib/src/parsers/eq_configuration.rs @@ -0,0 +1,55 @@ +use nom::{ + combinator::{map, map_opt}, + error::context, + number::complete::le_u16, + sequence::pair, +}; + +use crate::models::{EQProfile, MonoEQConfiguration, StereoEQ, StereoEQConfiguration}; + +use super::{parse_mono_eq, parse_stereo_eq, SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_stereo_eq_configuration<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_stereo_eq_config", + map( + pair(parse_eq_profile, parse_stereo_eq), + |(profile, eq)| match profile { + EQProfile::Custom => StereoEQConfiguration { profile, eq }, + _ => StereoEQConfiguration { + profile, + eq: StereoEQ { + left: profile.eq(), + right: profile.eq(), + }, + }, + }, + ), + )(bytes) +} + +pub fn parse_mono_eq_configuration<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_mono_eq_config", + map( + pair(parse_eq_profile, parse_mono_eq), + |(profile, eq)| match profile { + EQProfile::Custom => MonoEQConfiguration { profile, eq }, + _ => MonoEQConfiguration { + profile, + eq: profile.eq(), + }, + }, + ), + )(bytes) +} + +fn parse_eq_profile<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_mono_eq_config", map_opt(le_u16, EQProfile::from_id))(bytes) +} diff --git a/soundcore-lib/src/parsers/game_mode.rs b/soundcore-lib/src/parsers/game_mode.rs new file mode 100644 index 0000000..e69de29 diff --git a/soundcore-lib/src/parsers/gender.rs b/soundcore-lib/src/parsers/gender.rs new file mode 100644 index 0000000..985ddac --- /dev/null +++ b/soundcore-lib/src/parsers/gender.rs @@ -0,0 +1,13 @@ +use nom::combinator::map; +use nom::error::context; +use nom::number::complete::le_u8; + +use crate::models::Gender; + +use super::{SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_gender<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_gender", map(le_u8, Gender::from_u8))(bytes) +} diff --git a/soundcore-lib/src/parsers/hearid.rs b/soundcore-lib/src/parsers/hearid.rs new file mode 100644 index 0000000..63c105f --- /dev/null +++ b/soundcore-lib/src/parsers/hearid.rs @@ -0,0 +1,72 @@ +use nom::{ + bytes::complete::take, + combinator::map, + error::context, + number::complete::{le_i32, le_u8}, + sequence::tuple, +}; + +use crate::models::{BaseHearID, CustomHearID, HearIDMusicType, HearIDType, MonoEQ, StereoEQ}; + +use super::{ + parse_bool, parse_mono_eq, parse_stereo_eq, SoundcoreParseError, SoundcoreParseResult, +}; + +pub fn parse_base_hear_id<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_base_hear_id", + map( + tuple((parse_bool, parse_stereo_eq, le_i32)), + |(enabled, eq, time)| BaseHearID { + enabled, + values: eq, + time, + }, + ), + )(bytes) +} +pub fn parse_custom_hear_id<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_custom_hear_id", + map( + tuple(( + parse_base_hear_id, + parse_type, + parse_music_type, + take(8usize), + parse_mono_eq, + )), + |(base, hearid_type, hearid_music_type, l_eq, r_eq)| { + let eq = match l_eq[0] { + 255 => None, + _ => Some(StereoEQ { + left: MonoEQ::from_bytes(l_eq.try_into().unwrap()), + right: r_eq, + }), + }; + CustomHearID { + base, + hearid_type, + hearid_music_type, + custom_values: eq, + } + }, + ), + )(bytes) +} + +fn parse_type<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_hear_id_type", map(le_u8, HearIDType))(bytes) +} + +fn parse_music_type<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_hear_id_music_type", map(le_u8, HearIDMusicType))(bytes) +} diff --git a/soundcore-lib/src/parsers/packet_header.rs b/soundcore-lib/src/parsers/packet_header.rs new file mode 100644 index 0000000..bd0a6c4 --- /dev/null +++ b/soundcore-lib/src/parsers/packet_header.rs @@ -0,0 +1,114 @@ +use nom::{ + bytes::complete::take, + combinator::{map, map_opt}, + error::context, + number::complete::le_u16, + sequence::tuple, +}; + +use crate::models::{ResponsePacketHeader, ResponsePacketKind}; + +use super::{SoundcoreParseError, SoundcoreParseResult}; + +/* We can use Generic Arg Infer "#![feature(generic_arg_infer)]" once https://github.com/rust-lang/rust/issues/85077 is stabilized */ +/* This also could be dynamically be created, since the bytes match the command id bytes */ +const PACKET_MAP: [(&[u8; 2], ResponsePacketKind); 10] = [ + (&[0xFF, 0xFF], ResponsePacketKind::Unknown), + /* Updates */ + (&[0x01, 0x01], ResponsePacketKind::StateUpdate), + (&[0x01, 0x03], ResponsePacketKind::BattLevelUpdate), + (&[0x01, 0x04], ResponsePacketKind::BattChargingUpdate), + (&[0x01, 0x05], ResponsePacketKind::InfoUpdate), + (&[0x01, 0x7F], ResponsePacketKind::LDACUpdate), + (&[0x06, 0x01], ResponsePacketKind::SoundModeUpdate), + /* Acks */ + (&[0x06, 0x81], ResponsePacketKind::SetSoundModeAck), + (&[0x02, 0x81], ResponsePacketKind::SetEqAck), + (&[0x02, 0x83], ResponsePacketKind::SetEqDrcAck), +]; + +pub fn parse_packet_header<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_packet_header", + map( + tuple(( + parse_packet_prefix, + parse_packet_kind, + context("header_length", le_u16), // TODO: extract this to PacketHeaderSuffix? + )), + |(_, packet_kind, size)| ResponsePacketHeader { + kind: packet_kind, + length: size, + }, + ), + )(bytes) +} + +fn parse_packet_kind<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_packet_header", + map_opt(take(2usize), |bytes: &[u8]| { + PACKET_MAP + .iter() + .find(|(map_bytes, _)| map_bytes == &bytes) + .map(|(_, packet_kind)| *packet_kind) + }), + )(bytes) +} + +fn parse_packet_prefix<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult, E> { + /* If any other prefixes are found, they can be added here */ + context( + "parse_packet_header", + map(take(5usize), |prefix: &[u8]| match prefix { + [0x09, 0xFF, 0x00, 0x00, 0x01] => Ok(()), + _ => Err(()), + }), + )(bytes) +} + +#[cfg(test)] +mod packet_header_parser { + use crate::models::{ResponsePacketHeader, ResponsePacketKind}; + + #[test] + fn test_parse_complete_packet_header() { + let bytes = [0x09, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x05, 0x00]; + let result = super::parse_packet_header::>(&bytes); + assert!(result.is_ok()); + let (remaining, packet_kind) = result.unwrap(); + assert_eq!(remaining, &[]); + assert_eq!( + packet_kind, + ResponsePacketHeader { + kind: ResponsePacketKind::Unknown, + length: 0x05 + } + ); + } + + #[test] + fn test_parse_packet_kind() { + let bytes = [0xFF, 0xFF]; + let result = super::parse_packet_kind::>(&bytes); + assert!(result.is_ok()); + let (remaining, packet_kind) = result.unwrap(); + assert_eq!(remaining, &[]); + assert_eq!(packet_kind, ResponsePacketKind::Unknown); + } + + #[test] + fn test_parse_packet_prefix() { + let bytes = [0x09, 0xFF, 0x00, 0x00, 0x01]; + let result = super::parse_packet_prefix::>(&bytes); + assert!(result.is_ok()); + let (remaining, _) = result.unwrap(); + assert_eq!(remaining, &[]); + } +} diff --git a/soundcore-lib/src/parsers/serial.rs b/soundcore-lib/src/parsers/serial.rs new file mode 100644 index 0000000..7402533 --- /dev/null +++ b/soundcore-lib/src/parsers/serial.rs @@ -0,0 +1,23 @@ +use nom::{combinator::map, error::context}; + +use crate::models::SerialNumber; + +use super::{base::parse_str, SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_serial_number<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_serial", map(parse_str(16usize), SerialNumber::from))(bytes) +} + +#[cfg(test)] +mod serial_parser { + + #[test] + fn parses_valid_serial() { + let bytes = b"AC12F3B4D5E6A7B8"; + let expected = Ok((&b""[..], "AC12F3B4D5E6A7B8".into())); + let actual = super::parse_serial_number::>(bytes); + assert_eq!(expected, actual); + } +} diff --git a/soundcore-lib/src/parsers/side_tone.rs b/soundcore-lib/src/parsers/side_tone.rs new file mode 100644 index 0000000..d340d54 --- /dev/null +++ b/soundcore-lib/src/parsers/side_tone.rs @@ -0,0 +1,12 @@ +use nom::combinator::map; +use nom::error::context; + +use crate::models::SideTone; + +use super::{parse_bool, SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_side_tone<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_side_tone", map(parse_bool, SideTone::from))(bytes) +} diff --git a/soundcore-lib/src/parsers/sound_mode.rs b/soundcore-lib/src/parsers/sound_mode.rs new file mode 100644 index 0000000..d595e49 --- /dev/null +++ b/soundcore-lib/src/parsers/sound_mode.rs @@ -0,0 +1,92 @@ +use nom::{ + combinator::{map, map_opt}, + error::context, + number::complete::le_u8, + sequence::tuple, +}; + +use crate::models::{ANCMode, CurrentSoundMode, CustomANC, SoundMode, TransparencyMode}; + +use super::{SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_sound_mode<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_sound_mode", + map( + tuple(( + parse_curr_sound_mode, + parse_anc_mode, + parse_trans_mode, + parse_custom_anc, + )), + |(current, anc_mode, trans_mode, custom_anc)| SoundMode { + current, + anc_mode, + trans_mode, + custom_anc, + }, + ), + )(bytes) +} + +fn parse_curr_sound_mode<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_curr_sound_mode", + map_opt(le_u8, CurrentSoundMode::from_u8), + )(bytes) +} + +fn parse_anc_mode<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_anc_mode", map_opt(le_u8, ANCMode::from_u8))(bytes) +} + +fn parse_trans_mode<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context( + "parse_trans_mode", + map_opt(le_u8, TransparencyMode::from_u8), + )(bytes) +} + +fn parse_custom_anc<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_custom_anc", map(le_u8, CustomANC::from_u8))(bytes) +} + +#[cfg(test)] +mod anc_parser { + use super::*; + use crate::models::SoundMode; + + #[test] + fn parses_valid_bytes() { + let input = SoundMode { + current: CurrentSoundMode::ANC, + anc_mode: ANCMode::Outdoor, + trans_mode: TransparencyMode::Vocal, + custom_anc: CustomANC::from_u8(0x10), + } + .to_bytes(); + + let expected = Ok(( + &b""[..], + SoundMode { + current: CurrentSoundMode::ANC, + anc_mode: ANCMode::Outdoor, + trans_mode: TransparencyMode::Vocal, + custom_anc: CustomANC::from_u8(0x10), + }, + )); + let output = parse_sound_mode::>(&input); + + assert_eq!(expected, output); + } +} diff --git a/soundcore-lib/src/parsers/touch_tone.rs b/soundcore-lib/src/parsers/touch_tone.rs new file mode 100644 index 0000000..0732403 --- /dev/null +++ b/soundcore-lib/src/parsers/touch_tone.rs @@ -0,0 +1,9 @@ +use super::{base::parse_bool, SoundcoreParseError, SoundcoreParseResult}; +use crate::models::TouchTone; +use nom::{combinator::map, error::context}; + +pub fn parse_touch_tone<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_touch_tone", map(parse_bool, TouchTone::from))(bytes) +} diff --git a/soundcore-lib/src/parsers/wear_detection.rs b/soundcore-lib/src/parsers/wear_detection.rs new file mode 100644 index 0000000..35b5c44 --- /dev/null +++ b/soundcore-lib/src/parsers/wear_detection.rs @@ -0,0 +1,11 @@ +use nom::{combinator::map, error::context}; + +use crate::models::WearDetection; + +use super::{base::parse_bool, SoundcoreParseError, SoundcoreParseResult}; + +pub fn parse_wear_detection<'a, E: SoundcoreParseError<'a>>( + bytes: &'a [u8], +) -> SoundcoreParseResult { + context("parse_wear_detection", map(parse_bool, WearDetection::from))(bytes) +} diff --git a/soundcore-lib/tests/a3951.rs b/soundcore-lib/tests/a3951.rs new file mode 100644 index 0000000..a885d58 --- /dev/null +++ b/soundcore-lib/tests/a3951.rs @@ -0,0 +1,20 @@ +use soundcore_lib::packets::ResponsePacket; + +const STATE_UPDATE_BYTES: [u8; 97] = [ + 9, 255, 0, 0, 1, 1, 1, 97, 0, 1, 1, 5, 5, 1, 0, 254, 254, 160, 150, 130, 120, 120, 120, 120, + 120, 160, 150, 130, 120, 120, 120, 120, 120, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 99, 1, 84, 1, 102, 1, 84, 0, 1, 0, 0, 0, + 1, 1, 6, 0, 1, 0, 0, 0, 0, 242, +]; + +#[test] +fn parse_a3951_state_update() { + let packet = ResponsePacket::from_bytes(&STATE_UPDATE_BYTES).unwrap(); + match packet { + ResponsePacket::DeviceState(_state) => { + todo!() + } + _ => panic!("Parsed as wrong packet type"), + } +} diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 47ff426..a312d75 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -776,6 +776,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enumflags2" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + [[package]] name = "fastrand" version = "1.8.0" @@ -1153,7 +1174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64" dependencies = [ "anyhow", - "heck 0.4.0", + "heck 0.4.1", "proc-macro-crate", "proc-macro-error", "proc-macro2", @@ -1292,9 +1313,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -1819,6 +1840,12 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.5.4" @@ -1913,6 +1940,16 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "notify-rust" version = "4.5.10" @@ -2509,7 +2546,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "276470f7f281b0ed53d2ae42dd52b4a8d08853a3c70e7fe95882acbb98a6ae94" dependencies = [ "bytes", - "heck 0.4.0", + "heck 0.4.1", "itertools", "lazy_static", "log", @@ -3105,9 +3142,12 @@ dependencies = [ "async-trait", "bluetooth-lib", "dialoguer", + "enumflags2", "log", + "nom", "phf 0.11.1", "serde", + "strum 0.25.0", "thiserror", "tokio", "typeshare", @@ -3215,7 +3255,16 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7ac893c7d471c8a21f31cfe213ec4f6d9afeed25537c772e08ef3f005f8729e" dependencies = [ - "strum_macros", + "strum_macros 0.22.0", +] + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros 0.25.2", ] [[package]] @@ -3230,6 +3279,19 @@ dependencies = [ "syn 1.0.103", ] +[[package]] +name = "strum_macros" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.15", +] + [[package]] name = "syn" version = "1.0.103" @@ -3278,7 +3340,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" dependencies = [ "cfg-expr 0.11.0", - "heck 0.4.0", + "heck 0.4.1", "pkg-config", "toml", "version-compare 0.1.1", @@ -3360,7 +3422,7 @@ dependencies = [ "glib", "glob", "gtk", - "heck 0.4.0", + "heck 0.4.1", "http", "ignore", "notify-rust", @@ -3404,7 +3466,7 @@ checksum = "8807c85d656b2b93927c19fe5a5f1f1f348f96c2de8b90763b3c2d561511f9b4" dependencies = [ "anyhow", "cargo_toml", - "heck 0.4.0", + "heck 0.4.1", "json-patch", "semver 1.0.14", "serde_json", @@ -3444,7 +3506,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069319e5ecbe653a799b94b0690d9f9bf5d00f7b1d3989aa331c524d4e354075" dependencies = [ - "heck 0.4.0", + "heck 0.4.1", "proc-macro2", "quote", "syn 1.0.103", @@ -3518,7 +3580,7 @@ dependencies = [ "brotli", "ctor", "glob", - "heck 0.4.0", + "heck 0.4.1", "html5ever", "infer", "json-patch", @@ -3544,7 +3606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c58de036c4d2e20717024de2a3c4bf56c301f07b21bc8ef9b57189fce06f1f3b" dependencies = [ "quick-xml", - "strum", + "strum 0.22.0", "windows 0.39.0", ] diff --git a/src-tauri/build.rs b/src-tauri/build.rs index b384d19..dcd420a 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,6 +1,6 @@ use std::{ env, - path::{Path, PathBuf}, + path::{Path}, process::Command, }; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index fc50be5..f413c0d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -8,7 +8,7 @@ use bluetooth_lib::Scanner; use frontend_types::BthScanResult; use soundcore_lib::base::SoundcoreDevice; use soundcore_lib::types::{SupportedModels, SOUNDCORE_NAME_MODEL_MAP}; -use std::io::Stdout; + use std::sync::Arc; use tauri::async_runtime::{Mutex, RwLock}; use tauri::Manager; @@ -39,7 +39,6 @@ async fn scan_for_devices() -> Vec { if !btdevice.connected || !SOUNDCORE_NAME_MODEL_MAP .keys() - .into_iter() .any(|name| btdevice.name.contains(name)) { return;