diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
deleted file mode 100644
index 5852c07..0000000
--- a/.github/workflows/rust.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: Rust
-
-on:
- push:
- branches: [ "main", "vin" ]
- pull_request:
- branches: [ "main" ]
-
-env:
- CARGO_TERM_COLOR: always
-
-jobs:
- build:
-
- runs-on: windows-latest
-
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
- - name: Download and move VPIC database
- run: |
- curl -L -o data/vpic.sqlite "https://www.dropbox.com/scl/fi/qt21pwczi1pyv7syh05cy/vpic.sqlite?rlkey=zk0qh8q1mn6wyrjm8njfn61r0&e=1&st=ef0qnpt8&dl=1"
- - name: Build
- run: cargo build
- - name: Tests
- run: cargo test
- - name: Generate Docs
- run: cargo doc --no-deps
- - name: Format
- run: cargo fmt
- - name: Clippy
- run: cargo clippy -- -D warnings
diff --git a/.gitignore b/.gitignore
index 0f84cc9..d16cbeb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,28 @@
-/target
-/.vscode
\ No newline at end of file
+/backend/target/
+target/
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/Cargo.lock b/Cargo.lock
index f94ac69..35ee272 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,21 @@
# It is not intended for manual editing.
version = 4
+[[package]]
+name = "addr2line"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -11,6 +26,21 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "alloc-no-stdlib"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
+
+[[package]]
+name = "alloc-stdlib"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
+dependencies = [
+ "alloc-no-stdlib",
+]
+
[[package]]
name = "android-tzdata"
version = "0.1.1"
@@ -26,12 +56,75 @@ dependencies = [
"libc",
]
+[[package]]
+name = "anyhow"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+
+[[package]]
+name = "atk"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd"
+dependencies = [
+ "atk-sys",
+ "bitflags 1.3.2",
+ "glib",
+ "libc",
+]
+
+[[package]]
+name = "atk-sys"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 6.2.2",
+]
+
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+[[package]]
+name = "backtrace"
+version = "0.3.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-targets 0.52.6",
+]
+
+[[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.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -40,640 +133,4133 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.9.0"
+version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
-name = "bumpalo"
-version = "3.17.0"
+name = "block"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
-name = "cc"
-version = "1.2.20"
+name = "block-buffer"
+version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
- "shlex",
+ "generic-array",
]
[[package]]
-name = "cfg-if"
-version = "1.0.0"
+name = "brotli"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+ "brotli-decompressor",
+]
[[package]]
-name = "chrono"
-version = "0.4.41"
+name = "brotli-decompressor"
+version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
+checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd"
dependencies = [
- "android-tzdata",
- "iana-time-zone",
- "js-sys",
- "num-traits",
- "wasm-bindgen",
- "windows-link",
+ "alloc-no-stdlib",
+ "alloc-stdlib",
]
[[package]]
-name = "core-foundation"
-version = "0.10.0"
+name = "bstr"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
+checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
- "core-foundation-sys",
- "libc",
+ "memchr",
+ "serde",
]
[[package]]
-name = "core-foundation-sys"
-version = "0.8.7"
+name = "bumpalo"
+version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
-name = "equivalent"
-version = "1.0.2"
+name = "bytemuck"
+version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
[[package]]
-name = "evalexpr"
-version = "12.0.2"
+name = "byteorder"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02a3229bec56a977f174b32fe7b8d89e8c79ebb4493d10ad763b6676dc2dc0c9"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
-name = "hashbrown"
-version = "0.15.3"
+name = "bytes"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
-name = "iana-time-zone"
-version = "0.1.63"
+name = "cairo-rs"
+version = "0.15.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
+checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc"
dependencies = [
- "android_system_properties",
- "core-foundation-sys",
- "iana-time-zone-haiku",
- "js-sys",
- "log",
- "wasm-bindgen",
- "windows-core",
+ "bitflags 1.3.2",
+ "cairo-sys-rs",
+ "glib",
+ "libc",
+ "thiserror 1.0.69",
]
[[package]]
-name = "iana-time-zone-haiku"
-version = "0.1.2"
+name = "cairo-sys-rs"
+version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8"
dependencies = [
- "cc",
+ "glib-sys",
+ "libc",
+ "system-deps 6.2.2",
]
[[package]]
-name = "indexmap"
-version = "2.9.0"
+name = "cargo_toml"
+version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
+checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838"
dependencies = [
- "equivalent",
- "hashbrown",
+ "serde",
+ "toml 0.7.8",
]
[[package]]
-name = "io-kit-sys"
-version = "0.4.1"
+name = "cc"
+version = "1.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b"
+checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
dependencies = [
- "core-foundation-sys",
- "mach2",
+ "shlex",
]
[[package]]
-name = "itoa"
-version = "1.0.15"
+name = "cesu8"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
-name = "js-sys"
-version = "0.3.77"
+name = "cfb"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f"
dependencies = [
- "once_cell",
- "wasm-bindgen",
+ "byteorder",
+ "fnv",
+ "uuid",
]
[[package]]
-name = "libc"
-version = "0.2.172"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
-
-[[package]]
-name = "libudev"
-version = "0.3.0"
+name = "cfg-expr"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0"
+checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7"
dependencies = [
- "libc",
- "libudev-sys",
+ "smallvec",
]
[[package]]
-name = "libudev-sys"
-version = "0.1.4"
+name = "cfg-expr"
+version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
+checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
dependencies = [
- "libc",
- "pkg-config",
+ "smallvec",
+ "target-lexicon",
]
[[package]]
-name = "log"
-version = "0.4.27"
+name = "cfg-if"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
-name = "mach2"
-version = "0.4.2"
+name = "chrono"
+version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
+checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
- "libc",
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "serde",
+ "wasm-bindgen",
+ "windows-link",
]
[[package]]
-name = "memchr"
-version = "2.7.4"
+name = "cocoa"
+version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a"
+dependencies = [
+ "bitflags 1.3.2",
+ "block",
+ "cocoa-foundation",
+ "core-foundation 0.9.4",
+ "core-graphics",
+ "foreign-types",
+ "libc",
+ "objc",
+]
[[package]]
-name = "nix"
-version = "0.26.4"
+name = "cocoa-foundation"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7"
dependencies = [
"bitflags 1.3.2",
- "cfg-if",
+ "block",
+ "core-foundation 0.9.4",
+ "core-graphics-types",
"libc",
+ "objc",
]
[[package]]
-name = "num-traits"
-version = "0.2.19"
+name = "color_quant"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
-dependencies = [
- "autocfg",
-]
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
-name = "num_enum"
-version = "0.7.3"
+name = "combine"
+version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
- "num_enum_derive",
+ "bytes",
+ "memchr",
]
[[package]]
-name = "num_enum_derive"
-version = "0.7.3"
+name = "convert_case"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
- "proc-macro-crate",
- "proc-macro2",
- "quote",
- "syn",
+ "core-foundation-sys",
+ "libc",
]
[[package]]
-name = "obdium"
-version = "0.1.0"
+name = "core-foundation"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
dependencies = [
- "chrono",
- "evalexpr",
- "num_enum",
- "regex",
- "serde",
- "serde_json",
- "serialport",
- "sqlite",
- "thiserror 2.0.12",
+ "core-foundation-sys",
+ "libc",
]
[[package]]
-name = "once_cell"
-version = "1.21.3"
+name = "core-foundation-sys"
+version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
-name = "pkg-config"
-version = "0.3.32"
+name = "core-graphics"
+version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
[[package]]
-name = "proc-macro-crate"
-version = "3.3.0"
+name = "core-graphics-types"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
dependencies = [
- "toml_edit",
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "libc",
]
[[package]]
-name = "proc-macro2"
-version = "1.0.95"
+name = "cpufeatures"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
- "unicode-ident",
+ "libc",
]
[[package]]
-name = "quote"
-version = "1.0.40"
+name = "crc32fast"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
- "proc-macro2",
+ "cfg-if",
]
[[package]]
-name = "regex"
-version = "1.11.1"
+name = "crossbeam-channel"
+version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
+ "crossbeam-utils",
]
[[package]]
-name = "regex-automata"
-version = "0.4.9"
+name = "crossbeam-deque"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
+ "crossbeam-epoch",
+ "crossbeam-utils",
]
[[package]]
-name = "regex-syntax"
-version = "0.8.5"
+name = "crossbeam-epoch"
+version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
[[package]]
-name = "rustversion"
-version = "1.0.20"
+name = "crossbeam-utils"
+version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
-name = "ryu"
-version = "1.0.20"
+name = "crypto-common"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
[[package]]
-name = "scopeguard"
-version = "1.2.0"
+name = "cssparser"
+version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a"
+dependencies = [
+ "cssparser-macros",
+ "dtoa-short",
+ "itoa 0.4.8",
+ "matches",
+ "phf 0.8.0",
+ "proc-macro2",
+ "quote",
+ "smallvec",
+ "syn 1.0.109",
+]
[[package]]
-name = "serde"
-version = "1.0.219"
+name = "cssparser-macros"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [
- "serde_derive",
+ "quote",
+ "syn 2.0.101",
]
[[package]]
-name = "serde_derive"
-version = "1.0.219"
+name = "ctor"
+version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
+dependencies = [
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "darling"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
+dependencies = [
+ "fnv",
+ "ident_case",
"proc-macro2",
"quote",
- "syn",
+ "strsim",
+ "syn 2.0.101",
]
[[package]]
-name = "serde_json"
-version = "1.0.140"
+name = "darling_macro"
+version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
+checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
- "itoa",
- "memchr",
- "ryu",
- "serde",
+ "darling_core",
+ "quote",
+ "syn 2.0.101",
]
[[package]]
-name = "serialport"
-version = "4.7.1"
+name = "deranged"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2daa7abb9b965493e3c8f4184c6f46435484ff2538a332b886788cf6768b927b"
+checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [
- "bitflags 2.9.0",
- "cfg-if",
- "core-foundation",
- "core-foundation-sys",
- "io-kit-sys",
- "libudev",
- "mach2",
- "nix",
- "scopeguard",
- "unescaper",
- "winapi",
+ "powerfmt",
+ "serde",
]
[[package]]
-name = "shlex"
-version = "1.3.0"
+name = "derive_more"
+version = "0.99.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 2.0.101",
+]
[[package]]
-name = "sqlite"
-version = "0.37.0"
+name = "digest"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f66e9c01a11936154f3910dbba732c01f8b591543bc4d6672bddee79fd9c4783"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
- "sqlite3-sys",
+ "block-buffer",
+ "crypto-common",
]
[[package]]
-name = "sqlite3-src"
-version = "0.7.0"
+name = "dirs-next"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5b6d3c860886b0a33e69e421796a5f4a27f23597a182c2450f6d7ace5103120"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
- "cc",
- "pkg-config",
+ "cfg-if",
+ "dirs-sys-next",
]
[[package]]
-name = "sqlite3-sys"
-version = "0.18.0"
+name = "dirs-sys-next"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7781d97adc13a1d5081127a9ee29afad8427f3757bd984daf814d8265267039"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
- "sqlite3-src",
+ "libc",
+ "redox_users",
+ "winapi",
]
[[package]]
-name = "syn"
-version = "2.0.100"
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
- "unicode-ident",
+ "syn 2.0.101",
]
[[package]]
-name = "thiserror"
-version = "1.0.69"
+name = "dtoa"
+version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
-dependencies = [
- "thiserror-impl 1.0.69",
-]
+checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04"
[[package]]
-name = "thiserror"
-version = "2.0.12"
+name = "dtoa-short"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
dependencies = [
- "thiserror-impl 2.0.12",
+ "dtoa",
]
[[package]]
-name = "thiserror-impl"
-version = "1.0.69"
+name = "dunce"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
+[[package]]
+name = "embed-resource"
+version = "2.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b68b6f9f63a0b6a38bc447d4ce84e2b388f3ec95c99c641c8ff0dd3ef89a6379"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "cc",
+ "memchr",
+ "rustc_version",
+ "toml 0.8.22",
+ "vswhom",
+ "winreg",
]
[[package]]
-name = "thiserror-impl"
-version = "2.0.12"
+name = "embed_plist"
+version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "cfg-if",
]
[[package]]
-name = "toml_datetime"
-version = "0.6.9"
+name = "equivalent"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
-name = "toml_edit"
-version = "0.22.26"
+name = "errno"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
+checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
dependencies = [
- "indexmap",
- "toml_datetime",
- "winnow",
+ "libc",
+ "windows-sys 0.59.0",
]
[[package]]
-name = "unescaper"
-version = "0.1.5"
+name = "evalexpr"
+version = "12.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02a3229bec56a977f174b32fe7b8d89e8c79ebb4493d10ad763b6676dc2dc0c9"
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
dependencies = [
- "thiserror 1.0.69",
+ "simd-adler32",
]
[[package]]
-name = "unicode-ident"
-version = "1.0.18"
+name = "field-offset"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
+dependencies = [
+ "memoffset",
+ "rustc_version",
+]
[[package]]
-name = "wasm-bindgen"
-version = "0.2.100"
+name = "filetime"
+version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
dependencies = [
"cfg-if",
- "once_cell",
- "rustversion",
- "wasm-bindgen-macro",
+ "libc",
+ "libredox",
+ "windows-sys 0.59.0",
]
[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.100"
+name = "flate2"
+version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
dependencies = [
- "bumpalo",
- "log",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
+ "crc32fast",
+ "miniz_oxide",
]
[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.100"
+name = "fluent-uri"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d"
dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
+ "bitflags 1.3.2",
]
[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.100"
+name = "fnv"
+version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
+ "foreign-types-shared",
]
[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.100"
+name = "foreign-types-shared"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
- "unicode-ident",
+ "percent-encoding",
]
[[package]]
-name = "winapi"
-version = "0.3.9"
+name = "futf"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
+ "mac",
+ "new_debug_unreachable",
]
[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
+name = "futures-channel"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+]
[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
+name = "futures-core"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
-name = "windows-core"
-version = "0.61.0"
+name = "futures-executor"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
- "windows-implement",
- "windows-interface",
- "windows-link",
- "windows-result",
- "windows-strings",
+ "futures-core",
+ "futures-task",
+ "futures-util",
]
[[package]]
-name = "windows-implement"
-version = "0.60.0"
+name = "futures-io"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.101",
]
[[package]]
-name = "windows-interface"
-version = "0.59.1"
+name = "futures-task"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "futures-core",
+ "futures-macro",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
]
[[package]]
-name = "windows-link"
-version = "0.1.1"
+name = "fxhash"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
[[package]]
-name = "windows-result"
-version = "0.3.2"
+name = "gdk"
+version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
+checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8"
dependencies = [
- "windows-link",
+ "bitflags 1.3.2",
+ "cairo-rs",
+ "gdk-pixbuf",
+ "gdk-sys",
+ "gio",
+ "glib",
+ "libc",
+ "pango",
]
[[package]]
-name = "windows-strings"
-version = "0.4.0"
+name = "gdk-pixbuf"
+version = "0.15.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
+checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a"
dependencies = [
- "windows-link",
+ "bitflags 1.3.2",
+ "gdk-pixbuf-sys",
+ "gio",
+ "glib",
+ "libc",
]
[[package]]
-name = "winnow"
-version = "0.7.10"
+name = "gdk-pixbuf-sys"
+version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
+checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7"
dependencies = [
- "memchr",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "gdk-sys"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88"
+dependencies = [
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "pkg-config",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "gdkwayland-sys"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cca49a59ad8cfdf36ef7330fe7bdfbe1d34323220cc16a0de2679ee773aee2c2"
+dependencies = [
+ "gdk-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pkg-config",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "gdkx11-sys"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178"
+dependencies = [
+ "gdk-sys",
+ "glib-sys",
+ "libc",
+ "system-deps 6.2.2",
+ "x11",
+]
+
+[[package]]
+name = "generator"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
+dependencies = [
+ "cc",
+ "libc",
+ "log",
+ "rustversion",
+ "windows 0.48.0",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
+]
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "gio"
+version = "0.15.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b"
+dependencies = [
+ "bitflags 1.3.2",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "gio-sys",
+ "glib",
+ "libc",
+ "once_cell",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "gio-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 6.2.2",
+ "winapi",
+]
+
+[[package]]
+name = "glib"
+version = "0.15.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d"
+dependencies = [
+ "bitflags 1.3.2",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "glib-macros",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "once_cell",
+ "smallvec",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "glib-macros"
+version = "0.15.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a"
+dependencies = [
+ "anyhow",
+ "heck 0.4.1",
+ "proc-macro-crate 1.3.1",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4"
+dependencies = [
+ "libc",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
+
+[[package]]
+name = "globset"
+version = "0.4.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "log",
+ "regex-automata 0.4.9",
+ "regex-syntax 0.8.5",
+]
+
+[[package]]
+name = "gobject-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "gtk"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0"
+dependencies = [
+ "atk",
+ "bitflags 1.3.2",
+ "cairo-rs",
+ "field-offset",
+ "futures-channel",
+ "gdk",
+ "gdk-pixbuf",
+ "gio",
+ "glib",
+ "gtk-sys",
+ "gtk3-macros",
+ "libc",
+ "once_cell",
+ "pango",
+ "pkg-config",
+]
+
+[[package]]
+name = "gtk-sys"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84"
+dependencies = [
+ "atk-sys",
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gdk-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "gtk3-macros"
+version = "0.15.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d"
+dependencies = [
+ "anyhow",
+ "proc-macro-crate 1.3.1",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
+
+[[package]]
+name = "heck"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "html5ever"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
+dependencies = [
+ "log",
+ "mac",
+ "markup5ever",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "http"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa 1.0.15",
+]
+
+[[package]]
+name = "http-range"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "ico"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98"
+dependencies = [
+ "byteorder",
+ "png",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
+
+[[package]]
+name = "icu_properties"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "potential_utf",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
+
+[[package]]
+name = "icu_provider"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "ignore"
+version = "0.4.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
+dependencies = [
+ "crossbeam-deque",
+ "globset",
+ "log",
+ "memchr",
+ "regex-automata 0.4.9",
+ "same-file",
+ "walkdir",
+ "winapi-util",
+]
+
+[[package]]
+name = "image"
+version = "0.24.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "num-traits",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+ "serde",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.15.3",
+ "serde",
+]
+
+[[package]]
+name = "infer"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc"
+dependencies = [
+ "cfb",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "io-kit-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b"
+dependencies = [
+ "core-foundation-sys",
+ "mach2",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
+
+[[package]]
+name = "itoa"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "javascriptcore-rs"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c"
+dependencies = [
+ "bitflags 1.3.2",
+ "glib",
+ "javascriptcore-rs-sys",
+]
+
+[[package]]
+name = "javascriptcore-rs-sys"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 5.0.0",
+]
+
+[[package]]
+name = "jni"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c"
+dependencies = [
+ "cesu8",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror 1.0.69",
+ "walkdir",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "js-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "json-patch"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc"
+dependencies = [
+ "jsonptr",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "jsonptr"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627"
+dependencies = [
+ "fluent-uri",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "kuchikiki"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8"
+dependencies = [
+ "cssparser",
+ "html5ever",
+ "indexmap 1.9.3",
+ "matches",
+ "selectors",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.172"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
+
+[[package]]
+name = "libredox"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags 2.9.1",
+ "libc",
+ "redox_syscall",
+]
+
+[[package]]
+name = "libudev"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0"
+dependencies = [
+ "libc",
+ "libudev-sys",
+]
+
+[[package]]
+name = "libudev-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
+
+[[package]]
+name = "litemap"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+
+[[package]]
+name = "loom"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
+dependencies = [
+ "cfg-if",
+ "generator",
+ "scoped-tls",
+ "serde",
+ "serde_json",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "mac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+
+[[package]]
+name = "mach2"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "markup5ever"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
+dependencies = [
+ "log",
+ "phf 0.10.1",
+ "phf_codegen 0.10.0",
+ "string_cache",
+ "string_cache_codegen",
+ "tendril",
+]
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
+[[package]]
+name = "ndk"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4"
+dependencies = [
+ "bitflags 1.3.2",
+ "jni-sys",
+ "ndk-sys",
+ "num_enum 0.5.11",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
+name = "nix"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "nodrop"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
+dependencies = [
+ "num_enum_derive 0.5.11",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
+dependencies = [
+ "num_enum_derive 0.7.3",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
+dependencies = [
+ "proc-macro-crate 1.3.1",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
+dependencies = [
+ "proc-macro-crate 3.3.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "obdium"
+version = "0.1.0"
+dependencies = [
+ "chrono",
+ "evalexpr",
+ "num_enum 0.7.3",
+ "regex",
+ "serde",
+ "serde_json",
+ "serialport",
+ "sqlite",
+ "tauri",
+ "tauri-build",
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+ "objc_exception",
+]
+
+[[package]]
+name = "objc_exception"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "objc_id"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
+dependencies = [
+ "objc",
+]
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "open"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8"
+dependencies = [
+ "pathdiff",
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "pango"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f"
+dependencies = [
+ "bitflags 1.3.2",
+ "glib",
+ "libc",
+ "once_cell",
+ "pango-sys",
+]
+
+[[package]]
+name = "pango-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "pathdiff"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "phf"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+dependencies = [
+ "phf_macros 0.8.0",
+ "phf_shared 0.8.0",
+ "proc-macro-hack",
+]
+
+[[package]]
+name = "phf"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
+dependencies = [
+ "phf_shared 0.10.0",
+]
+
+[[package]]
+name = "phf"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
+dependencies = [
+ "phf_macros 0.11.3",
+ "phf_shared 0.11.3",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
+dependencies = [
+ "phf_generator 0.8.0",
+ "phf_shared 0.8.0",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
+dependencies = [
+ "phf_generator 0.10.0",
+ "phf_shared 0.10.0",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+dependencies = [
+ "phf_shared 0.8.0",
+ "rand 0.7.3",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
+dependencies = [
+ "phf_shared 0.10.0",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
+dependencies = [
+ "phf_shared 0.11.3",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
+dependencies = [
+ "phf_generator 0.8.0",
+ "phf_shared 0.8.0",
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
+dependencies = [
+ "phf_generator 0.11.3",
+ "phf_shared 0.11.3",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+dependencies = [
+ "siphasher 0.3.11",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
+dependencies = [
+ "siphasher 0.3.11",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
+dependencies = [
+ "siphasher 1.0.1",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "plist"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d"
+dependencies = [
+ "base64 0.22.1",
+ "indexmap 2.9.0",
+ "quick-xml",
+ "serde",
+ "time",
+]
+
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
+dependencies = [
+ "once_cell",
+ "toml_edit 0.19.15",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
+dependencies = [
+ "toml_edit 0.22.26",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.20+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom 0.1.16",
+ "libc",
+ "rand_chacha 0.2.2",
+ "rand_core 0.5.1",
+ "rand_hc",
+ "rand_pcg",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.16",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_pcg"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
+dependencies = [
+ "bitflags 2.9.1",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
+dependencies = [
+ "getrandom 0.2.16",
+ "libredox",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.9",
+ "regex-syntax 0.8.5",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.5",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
+dependencies = [
+ "bitflags 2.9.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
+
+[[package]]
+name = "ryu"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "selectors"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe"
+dependencies = [
+ "bitflags 1.3.2",
+ "cssparser",
+ "derive_more",
+ "fxhash",
+ "log",
+ "matches",
+ "phf 0.8.0",
+ "phf_codegen 0.8.0",
+ "precomputed-hash",
+ "servo_arc",
+ "smallvec",
+ "thin-slice",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.140"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
+dependencies = [
+ "indexmap 2.9.0",
+ "itoa 1.0.15",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_repr"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_with"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
+dependencies = [
+ "base64 0.22.1",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.9.0",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_with_macros",
+ "time",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "serialize-to-javascript"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5"
+dependencies = [
+ "serde",
+ "serde_json",
+ "serialize-to-javascript-impl",
+]
+
+[[package]]
+name = "serialize-to-javascript-impl"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "serialport"
+version = "4.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb0bc984f6af6ef8bab54e6cf2071579ee75b9286aa9f2319a0d220c28b0a2b"
+dependencies = [
+ "bitflags 2.9.1",
+ "cfg-if",
+ "core-foundation 0.10.0",
+ "core-foundation-sys",
+ "io-kit-sys",
+ "libudev",
+ "mach2",
+ "nix",
+ "scopeguard",
+ "unescaper",
+ "winapi",
+]
+
+[[package]]
+name = "servo_arc"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432"
+dependencies = [
+ "nodrop",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
+[[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
+[[package]]
+name = "siphasher"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
+
+[[package]]
+name = "soup2"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0"
+dependencies = [
+ "bitflags 1.3.2",
+ "gio",
+ "glib",
+ "libc",
+ "once_cell",
+ "soup2-sys",
+]
+
+[[package]]
+name = "soup2-sys"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf"
+dependencies = [
+ "bitflags 1.3.2",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 5.0.0",
+]
+
+[[package]]
+name = "sqlite"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f66e9c01a11936154f3910dbba732c01f8b591543bc4d6672bddee79fd9c4783"
+dependencies = [
+ "sqlite3-sys",
+]
+
+[[package]]
+name = "sqlite3-src"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5b6d3c860886b0a33e69e421796a5f4a27f23597a182c2450f6d7ace5103120"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
+[[package]]
+name = "sqlite3-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7781d97adc13a1d5081127a9ee29afad8427f3757bd984daf814d8265267039"
+dependencies = [
+ "sqlite3-src",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "state"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b"
+dependencies = [
+ "loom",
+]
+
+[[package]]
+name = "string_cache"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f"
+dependencies = [
+ "new_debug_unreachable",
+ "parking_lot",
+ "phf_shared 0.11.3",
+ "precomputed-hash",
+ "serde",
+]
+
+[[package]]
+name = "string_cache_codegen"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0"
+dependencies = [
+ "phf_generator 0.11.3",
+ "phf_shared 0.11.3",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "system-deps"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e"
+dependencies = [
+ "cfg-expr 0.9.1",
+ "heck 0.3.3",
+ "pkg-config",
+ "toml 0.5.11",
+ "version-compare 0.0.11",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+dependencies = [
+ "cfg-expr 0.15.8",
+ "heck 0.5.0",
+ "pkg-config",
+ "toml 0.8.22",
+ "version-compare 0.2.0",
+]
+
+[[package]]
+name = "tao"
+version = "0.16.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48d298c441a1da46e28e8ad8ec205aab7fd8cd71b9d10e05454224eef422e1ae"
+dependencies = [
+ "bitflags 1.3.2",
+ "cairo-rs",
+ "cc",
+ "cocoa",
+ "core-foundation 0.9.4",
+ "core-graphics",
+ "crossbeam-channel",
+ "dispatch",
+ "gdk",
+ "gdk-pixbuf",
+ "gdk-sys",
+ "gdkwayland-sys",
+ "gdkx11-sys",
+ "gio",
+ "glib",
+ "glib-sys",
+ "gtk",
+ "image",
+ "instant",
+ "jni",
+ "lazy_static",
+ "libc",
+ "log",
+ "ndk",
+ "ndk-context",
+ "ndk-sys",
+ "objc",
+ "once_cell",
+ "parking_lot",
+ "png",
+ "raw-window-handle",
+ "scopeguard",
+ "serde",
+ "tao-macros",
+ "unicode-segmentation",
+ "uuid",
+ "windows 0.39.0",
+ "windows-implement 0.39.0",
+ "x11-dl",
+]
+
+[[package]]
+name = "tao-macros"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "tar"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
+dependencies = [
+ "filetime",
+ "libc",
+ "xattr",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+
+[[package]]
+name = "tauri"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ae1f57c291a6ab8e1d2e6b8ad0a35ff769c9925deb8a89de85425ff08762d0c"
+dependencies = [
+ "anyhow",
+ "cocoa",
+ "dirs-next",
+ "dunce",
+ "embed_plist",
+ "encoding_rs",
+ "flate2",
+ "futures-util",
+ "getrandom 0.2.16",
+ "glib",
+ "glob",
+ "gtk",
+ "heck 0.5.0",
+ "http",
+ "ignore",
+ "log",
+ "objc",
+ "once_cell",
+ "open",
+ "percent-encoding",
+ "plist",
+ "rand 0.8.5",
+ "raw-window-handle",
+ "regex",
+ "semver",
+ "serde",
+ "serde_json",
+ "serde_repr",
+ "serialize-to-javascript",
+ "state",
+ "tar",
+ "tauri-macros",
+ "tauri-runtime",
+ "tauri-runtime-wry",
+ "tauri-utils",
+ "tempfile",
+ "thiserror 1.0.69",
+ "tokio",
+ "url",
+ "uuid",
+ "webkit2gtk",
+ "webview2-com",
+ "windows 0.39.0",
+]
+
+[[package]]
+name = "tauri-build"
+version = "1.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2db08694eec06f53625cfc6fff3a363e084e5e9a238166d2989996413c346453"
+dependencies = [
+ "anyhow",
+ "cargo_toml",
+ "dirs-next",
+ "heck 0.5.0",
+ "json-patch",
+ "semver",
+ "serde",
+ "serde_json",
+ "tauri-utils",
+ "tauri-winres",
+ "walkdir",
+]
+
+[[package]]
+name = "tauri-codegen"
+version = "1.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53438d78c4a037ffe5eafa19e447eea599bedfb10844cb08ec53c2471ac3ac3f"
+dependencies = [
+ "base64 0.21.7",
+ "brotli",
+ "ico",
+ "json-patch",
+ "plist",
+ "png",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "semver",
+ "serde",
+ "serde_json",
+ "sha2",
+ "tauri-utils",
+ "thiserror 1.0.69",
+ "time",
+ "uuid",
+ "walkdir",
+]
+
+[[package]]
+name = "tauri-macros"
+version = "1.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233988ac08c1ed3fe794cd65528d48d8f7ed4ab3895ca64cdaa6ad4d00c45c0b"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "tauri-codegen",
+ "tauri-utils",
+]
+
+[[package]]
+name = "tauri-runtime"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8066855882f00172935e3fa7d945126580c34dcbabab43f5d4f0c2398a67d47b"
+dependencies = [
+ "gtk",
+ "http",
+ "http-range",
+ "rand 0.8.5",
+ "raw-window-handle",
+ "serde",
+ "serde_json",
+ "tauri-utils",
+ "thiserror 1.0.69",
+ "url",
+ "uuid",
+ "webview2-com",
+ "windows 0.39.0",
+]
+
+[[package]]
+name = "tauri-runtime-wry"
+version = "0.14.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce361fec1e186705371f1c64ae9dd2a3a6768bc530d0a2d5e75a634bb416ad4d"
+dependencies = [
+ "cocoa",
+ "gtk",
+ "percent-encoding",
+ "rand 0.8.5",
+ "raw-window-handle",
+ "tauri-runtime",
+ "tauri-utils",
+ "uuid",
+ "webkit2gtk",
+ "webview2-com",
+ "windows 0.39.0",
+ "wry",
+]
+
+[[package]]
+name = "tauri-utils"
+version = "1.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c357952645e679de02cd35007190fcbce869b93ffc61b029f33fe02648453774"
+dependencies = [
+ "brotli",
+ "ctor",
+ "dunce",
+ "glob",
+ "heck 0.5.0",
+ "html5ever",
+ "infer",
+ "json-patch",
+ "kuchikiki",
+ "log",
+ "memchr",
+ "phf 0.11.3",
+ "proc-macro2",
+ "quote",
+ "semver",
+ "serde",
+ "serde_json",
+ "serde_with",
+ "thiserror 1.0.69",
+ "url",
+ "walkdir",
+ "windows-version",
+]
+
+[[package]]
+name = "tauri-winres"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb"
+dependencies = [
+ "embed-resource",
+ "toml 0.7.8",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
+dependencies = [
+ "fastrand",
+ "getrandom 0.3.3",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "tendril"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
+dependencies = [
+ "futf",
+ "mac",
+ "utf-8",
+]
+
+[[package]]
+name = "thin-slice"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl 2.0.12",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "time"
+version = "0.3.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
+dependencies = [
+ "deranged",
+ "itoa 1.0.15",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
+
+[[package]]
+name = "time-macros"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tokio"
+version = "1.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit 0.19.15",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit 0.22.26",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
+dependencies = [
+ "indexmap 2.9.0",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow 0.5.40",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
+dependencies = [
+ "indexmap 2.9.0",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_write",
+ "winnow 0.7.10",
+]
+
+[[package]]
+name = "toml_write"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
+
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "typenum"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
+
+[[package]]
+name = "unescaper"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c01d12e3a56a4432a8b436f293c25f4808bdf9e9f9f98f9260bba1f1bc5a1f26"
+dependencies = [
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "url"
+version = "2.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "uuid"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
+dependencies = [
+ "getrandom 0.3.3",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
+name = "version-compare"
+version = "0.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b"
+
+[[package]]
+name = "version-compare"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "vswhom"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b"
+dependencies = [
+ "libc",
+ "vswhom-sys",
+]
+
+[[package]]
+name = "vswhom-sys"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasi"
+version = "0.14.2+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "webkit2gtk"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370"
+dependencies = [
+ "bitflags 1.3.2",
+ "cairo-rs",
+ "gdk",
+ "gdk-sys",
+ "gio",
+ "gio-sys",
+ "glib",
+ "glib-sys",
+ "gobject-sys",
+ "gtk",
+ "gtk-sys",
+ "javascriptcore-rs",
+ "libc",
+ "once_cell",
+ "soup2",
+ "webkit2gtk-sys",
+]
+
+[[package]]
+name = "webkit2gtk-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3"
+dependencies = [
+ "atk-sys",
+ "bitflags 1.3.2",
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gdk-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "gtk-sys",
+ "javascriptcore-rs-sys",
+ "libc",
+ "pango-sys",
+ "pkg-config",
+ "soup2-sys",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "webview2-com"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178"
+dependencies = [
+ "webview2-com-macros",
+ "webview2-com-sys",
+ "windows 0.39.0",
+ "windows-implement 0.39.0",
+]
+
+[[package]]
+name = "webview2-com-macros"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "webview2-com-sys"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7"
+dependencies = [
+ "regex",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+ "windows 0.39.0",
+ "windows-bindgen",
+ "windows-metadata",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a"
+dependencies = [
+ "windows-implement 0.39.0",
+ "windows_aarch64_msvc 0.39.0",
+ "windows_i686_gnu 0.39.0",
+ "windows_i686_msvc 0.39.0",
+ "windows_x86_64_gnu 0.39.0",
+ "windows_x86_64_msvc 0.39.0",
+]
+
+[[package]]
+name = "windows"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-bindgen"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41"
+dependencies = [
+ "windows-metadata",
+ "windows-tokens",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
+dependencies = [
+ "windows-implement 0.60.0",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7"
+dependencies = [
+ "syn 1.0.109",
+ "windows-tokens",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+
+[[package]]
+name = "windows-metadata"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278"
+
+[[package]]
+name = "windows-result"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "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]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-tokens"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597"
+
+[[package]]
+name = "windows-version"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "winnow"
+version = "0.5.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "winnow"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "winreg"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+dependencies = [
+ "bitflags 2.9.1",
+]
+
+[[package]]
+name = "writeable"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
+
+[[package]]
+name = "wry"
+version = "0.24.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c55c80b12287eb1ff7c365fc2f7a5037cb6181bd44c9fce81c8d1cf7605ffad6"
+dependencies = [
+ "base64 0.13.1",
+ "block",
+ "cocoa",
+ "core-graphics",
+ "crossbeam-channel",
+ "dunce",
+ "gdk",
+ "gio",
+ "glib",
+ "gtk",
+ "html5ever",
+ "http",
+ "kuchikiki",
+ "libc",
+ "log",
+ "objc",
+ "objc_id",
+ "once_cell",
+ "serde",
+ "serde_json",
+ "sha2",
+ "soup2",
+ "tao",
+ "thiserror 1.0.69",
+ "url",
+ "webkit2gtk",
+ "webkit2gtk-sys",
+ "webview2-com",
+ "windows 0.39.0",
+ "windows-implement 0.39.0",
+]
+
+[[package]]
+name = "x11"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "x11-dl"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
+dependencies = [
+ "libc",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "xattr"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e"
+dependencies = [
+ "libc",
+ "rustix",
+]
+
+[[package]]
+name = "yoke"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "synstructure",
+]
+
+[[package]]
+name = "zerotrie"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
]
diff --git a/README.md b/README.md
index 19869f5..2d9d925 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
OBDium
+
OBDium - GUI Branch
A Rust-based vehicle diagnostics tool designed to connect with ELM327 adapters, offering live OBD-II data, fault code analysis, and offline VIN decoding.
@@ -9,31 +9,31 @@
-
+
## Table of Contents
- [Overview](#overview)
- [Implementation](#implementation-and-logic)
- [Features](#features)
-- [Usage](#example-usage)
+- [Usage](#usage)
- [License](#license)
+- [Contributing](#contributing)
- [Project Structure](#project-structure)
- [Roadmap](#roadmap)
## Overview
-**OBDium** ( pronounced Oh-Bid-ium ), is a fast, extensible Rust-based diagnostic tool for interfacing with OBD-II systems via ELM327 serial adapters. It provides live access to vehicle sensor data, in-depth diagnostics, and accurate VIN decoding without relying on external crates for critical parsing logic.
+**OBDium** ( pronounced Oh-Bid-ium ), is a fast, modern and extensible Rust-based diagnostic tool for interfacing with OBD-II systems via ELM327 serial adapters fully offline. It provides live access to vehicle sensor data, in-depth diagnostics, and accurate VIN decoding without relying on external crates for critical parsing logic.
Our goal with OBDium is to fill a gap in the ecosystem, providing the best free, open-source, and easy-to-use vehicle diagnostics tool.
## Features
-
-- **🔌 Serial Communication:** Connects to ELM327 OBD-II adapters via serial port
+- **⚠️ View Troube Codes:** Read diagnostic trouble codes, including **Powertrain, Body, Chassis,** and **Network** alongside a description
- **🧠 Live Vehicle Metrics:** Reads and decodes various OBD-II PIDs (engine, fuel, air, exhaust, diagnostics, etc.) with plans for manufacturer specific PIDs soon
- **🔎 Advanced VIN Decoding:** In-depth VIN decoding using a custom parser and SQLite-backed lookups based off of the NHTSA's VPIC MSSQL database
-- **💾 SQLite-Backed Caching & Queries:** Persistent VIN metadata and decoded lookup results are stored locally for fast and offline operation
-- **🧪 Unit Tests:** Comprehensive unit tests for VIN decoding and database access ([`tests/vin.rs`](tests/vin.rs))
-- **⚙️ Error Handling and Resilience:** Gracefully handles common ELM327 quirks and serial errors with clear messages and fallbacks
+- **🔌 Serial Communication:** Connects to ELM327 OBD-II adapters via serial port
+- **📱 Modern GUI:** No more ugly and outdated native applications. Developed with modern web development technologies using Tauri with JS/HTML/CSS
+- **🖥️ Cross-platform:** Available on any operating system include both Linux and Windows
## Implementation and Logic
This project required extensive research into concepts like ELM327, the OBD-II protocol, and response decoding. Below is a brief explanation of the implementation and logic behind OBDium.
@@ -46,75 +46,60 @@ This project required extensive research into concepts like ELM327, the OBD-II p
For any questions about the implementation or logic behind OBDium, feel free to create a Discussion or open an Issue!
-## Try it yourself!
-
-Since commit 9a22b44, it is now possible to use previously recorded results for responses to simulate a vehicle responding to commands. **This means you don't even need an EML327 adapter for testing.**
-
-1. To get started, follow the [installation](#installation) steps.
-
-2. In [`src/main.rs`](src/main.rs), ensure the line `obd.record_requests(state)` is commented out or set to false, and `obd.replay_requests(state)` is uncommented or set to true (soon to be made more convienant in later version).
-
-Now, OBDium will use all the responses located in the [`data/requests.json`](data/requests.json) file.
-
-## Example Usage
-
-To try it with an ELM327 adapter paired with a real car:
-
-1. Connect your ELM327 adapter to your computer and note the serial port (e.g., `COM4` on Windows or `/dev/ttyUSB0` on Linux).
-
-```sh
-cargo run --release
-```
-
-Sample output:
-```
-Model year: 2018
-WMI: "KL4"
-WMI ID: 2069
-Key: "CJASB|JB660929"
-
-======================== VEHICLE MODEL INFO ========================
-Manufacturer Country: ...
-Manufacturer Name: ...
-Manufacturer Region: ...
-...
+## Installation
-======================== DIAGNOSTICS ========================
-Supported pids for ECUs
-ECU 7E8:
- 01 03 04 05 06 07 0A 0B 0C 0D ...
-Check engine light: false
-Number of trouble codes: 0
-OBD standard: JOBD and OBD-II
-Auxiliary input status: Inactive
-...
-```
+### Installer
+1. Head to the GitHub releases page [here](https://github.com/provrb/obdium/releases)
+2. Download the latest release for your operating system
+3. Extract and run the installer.
-## Installation
+### Command-Line
1. **Install Rust**
Download and install from [rust-lang.org](https://www.rust-lang.org/tools/install).
+2. **Install Tauri**
+ ```sh
+ cargo install tauri-cli --version 1.6.5
+ ```
+
2. **Clone the repository**
```sh
git clone https://github.com/provrb/obdium.git
cd obdium
```
- Ensure you correctly install the vpic.sqlite file from `/data/`.
-
3. **Build the project**
```sh
- cargo build --release
+ cargo tauri build
```
-4. **(Optional) Run tests**
- ```sh
- cargo test
- ```
+4. **Find the application**:
+ - The file built will be located in: `backend/target/release`
+ - MSI and NSIS installers will be located in: `backend/target/release/bundle`
5. **Prepare SQLite databases**
- Ensure the required SQLite databases are present in the `data/` directory (see [`src/vin/parser.rs`](src/vin/parser.rs) for expected paths). This will change to be more convenient in the future.
+ Ensure the required SQLite databases are present in the `data/` directory (see [`src/vin/parser.rs`](backend/src/vin/mod.rs), [`src/lib.rs`](backend/src/lib.rs) for expected paths). This will change to be more convenient in the future.
+
+## Usage
+
+### Connecting
+1. Run the application
+2. Connect your ELM327 adapter to your vehicles OBD-II port and device.
+3. Navigate to the **Connection** panel:
+ - Select the OBD-II protocol, serial port, and baud rate to use.
+ - If not serial ports appear, you can click the refresh button to reload serial ports.
+4. Click 'Connect'
+
+### Features
+- OBD data will be recorded in the **OBD Dashboard**
+- View diagnostic trouble codes in the **DTC** panel.
+- View graphs for live data in the **Graphs** panel.
+- Decode a VIN to receive model-specific information in the **VIN Decoding** panel.
+- To stop tracking a metric, click on the card it's being displayed in, in the **OBD Dashboard**
+ - Resume tracking by clicking on it again at the very bottom of the dashboard.
+- Modify preferences like units, privacy, or startup settings in the **Settings** panel.
+- View an index of all PIDs in the **PID List** panel.
## Contributing
@@ -128,24 +113,23 @@ To contribute:
## Project Structure
-- [`src/main.rs`](src/main.rs): Entry point and CLI logic
-- [`src/obd.rs`](src/obd.rs): OBD-II communication and protocol handling
-- [`src/pid/`](src/pid/mod.rs): PID modules for various OBD-II data groups
-- [`src/vin/`](src/vin/mod.rs): VIN decoding and database integration
-- [`tests/vin.rs`](tests/vin.rs): Unit tests for VIN decoding and database access
+### Backend
+- [`backend/`](/backend/): Backend logic include calculations and core functionality of the app
+- [`backend/src/vin/`](/backend/src/vin/mod.rs): VIN decoding and database integration
+- [`backend/src/obd.rs`](/backend/src/obd.rs): Main OBD interface
+- [`backend/src/bridge`](/backend/src/bridge/): Backend interface for the frontend
+
+### Frontend
+- [`frontend/`](/frontend/): Main GUI logic
+
## License
This code has minimal restrictions, such that any distributions are made free-of-cost. See [`LICENSE`](LICENSE) for details.
## Roadmap
-1. Finish VIN parsing functionality.
+1. ~~Finish VIN parsing functionality.~~
2. Fully functional user-interface with live graph view and the ability to send requests manually to the vehicle.
-3. DOCUMENT THE CRATE!!!!
-
----
+3. Support for Bluetooth and Wi-fi ELM connection
-**Note:**
-- Ensure your user account has permission to access the serial port.
-- The tool expects the SQLite databases to be present in the `data/` directory.
-- For more details on extending PID or VIN decoding, see the module files in [`src/pid/`](src/pid/mod.rs) and [`src/vin/`](src/vin/mod.rs).
\ No newline at end of file
+---
\ No newline at end of file
diff --git a/backend/Cargo.lock b/backend/Cargo.lock
new file mode 100644
index 0000000..c59a87e
--- /dev/null
+++ b/backend/Cargo.lock
@@ -0,0 +1,4266 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "addr2line"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "alloc-no-stdlib"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
+
+[[package]]
+name = "alloc-stdlib"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
+dependencies = [
+ "alloc-no-stdlib",
+]
+
+[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+
+[[package]]
+name = "atk"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd"
+dependencies = [
+ "atk-sys",
+ "bitflags 1.3.2",
+ "glib",
+ "libc",
+]
+
+[[package]]
+name = "atk-sys"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "backtrace"
+version = "0.3.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-targets 0.52.6",
+]
+
+[[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.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "brotli"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+ "brotli-decompressor",
+]
+
+[[package]]
+name = "brotli-decompressor"
+version = "4.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+]
+
+[[package]]
+name = "bstr"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
+
+[[package]]
+name = "bytemuck"
+version = "1.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
+
+[[package]]
+name = "cairo-rs"
+version = "0.15.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc"
+dependencies = [
+ "bitflags 1.3.2",
+ "cairo-sys-rs",
+ "glib",
+ "libc",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "cairo-sys-rs"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "cargo_toml"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838"
+dependencies = [
+ "serde",
+ "toml 0.7.8",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
+name = "cfb"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f"
+dependencies = [
+ "byteorder",
+ "fnv",
+ "uuid",
+]
+
+[[package]]
+name = "cfg-expr"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "cfg-expr"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "serde",
+ "wasm-bindgen",
+ "windows-link",
+]
+
+[[package]]
+name = "cocoa"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a"
+dependencies = [
+ "bitflags 1.3.2",
+ "block",
+ "cocoa-foundation",
+ "core-foundation 0.9.4",
+ "core-graphics",
+ "foreign-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "cocoa-foundation"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7"
+dependencies = [
+ "bitflags 1.3.2",
+ "block",
+ "core-foundation 0.9.4",
+ "core-graphics-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "core-graphics"
+version = "0.22.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "libc",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "cssparser"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a"
+dependencies = [
+ "cssparser-macros",
+ "dtoa-short",
+ "itoa 0.4.8",
+ "matches",
+ "phf 0.8.0",
+ "proc-macro2",
+ "quote",
+ "smallvec",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "cssparser-macros"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
+dependencies = [
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "ctor"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
+dependencies = [
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "darling"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "deranged"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
+dependencies = [
+ "powerfmt",
+ "serde",
+]
+
+[[package]]
+name = "derive_more"
+version = "0.99.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "dtoa"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04"
+
+[[package]]
+name = "dtoa-short"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
+dependencies = [
+ "dtoa",
+]
+
+[[package]]
+name = "dunce"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
+[[package]]
+name = "embed-resource"
+version = "2.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b68b6f9f63a0b6a38bc447d4ce84e2b388f3ec95c99c641c8ff0dd3ef89a6379"
+dependencies = [
+ "cc",
+ "memchr",
+ "rustc_version",
+ "toml 0.8.22",
+ "vswhom",
+ "winreg",
+]
+
+[[package]]
+name = "embed_plist"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "errno"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "evalexpr"
+version = "12.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02a3229bec56a977f174b32fe7b8d89e8c79ebb4493d10ad763b6676dc2dc0c9"
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "field-offset"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
+dependencies = [
+ "memoffset",
+ "rustc_version",
+]
+
+[[package]]
+name = "filetime"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "libredox",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "flate2"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fluent-uri"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
+dependencies = [
+ "mac",
+ "new_debug_unreachable",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-core",
+ "futures-macro",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "gdk"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8"
+dependencies = [
+ "bitflags 1.3.2",
+ "cairo-rs",
+ "gdk-pixbuf",
+ "gdk-sys",
+ "gio",
+ "glib",
+ "libc",
+ "pango",
+]
+
+[[package]]
+name = "gdk-pixbuf"
+version = "0.15.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a"
+dependencies = [
+ "bitflags 1.3.2",
+ "gdk-pixbuf-sys",
+ "gio",
+ "glib",
+ "libc",
+]
+
+[[package]]
+name = "gdk-pixbuf-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7"
+dependencies = [
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "gdk-sys"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88"
+dependencies = [
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "pkg-config",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "gdkwayland-sys"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cca49a59ad8cfdf36ef7330fe7bdfbe1d34323220cc16a0de2679ee773aee2c2"
+dependencies = [
+ "gdk-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pkg-config",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "gdkx11-sys"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178"
+dependencies = [
+ "gdk-sys",
+ "glib-sys",
+ "libc",
+ "system-deps 6.2.2",
+ "x11",
+]
+
+[[package]]
+name = "generator"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
+dependencies = [
+ "cc",
+ "libc",
+ "log",
+ "rustversion",
+ "windows 0.48.0",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
+]
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "gio"
+version = "0.15.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b"
+dependencies = [
+ "bitflags 1.3.2",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "gio-sys",
+ "glib",
+ "libc",
+ "once_cell",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "gio-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 6.2.2",
+ "winapi",
+]
+
+[[package]]
+name = "glib"
+version = "0.15.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d"
+dependencies = [
+ "bitflags 1.3.2",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "glib-macros",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "once_cell",
+ "smallvec",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "glib-macros"
+version = "0.15.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a"
+dependencies = [
+ "anyhow",
+ "heck 0.4.1",
+ "proc-macro-crate 1.3.1",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4"
+dependencies = [
+ "libc",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
+
+[[package]]
+name = "globset"
+version = "0.4.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "log",
+ "regex-automata 0.4.9",
+ "regex-syntax 0.8.5",
+]
+
+[[package]]
+name = "gobject-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "gtk"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0"
+dependencies = [
+ "atk",
+ "bitflags 1.3.2",
+ "cairo-rs",
+ "field-offset",
+ "futures-channel",
+ "gdk",
+ "gdk-pixbuf",
+ "gio",
+ "glib",
+ "gtk-sys",
+ "gtk3-macros",
+ "libc",
+ "once_cell",
+ "pango",
+ "pkg-config",
+]
+
+[[package]]
+name = "gtk-sys"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84"
+dependencies = [
+ "atk-sys",
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gdk-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "gtk3-macros"
+version = "0.15.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d"
+dependencies = [
+ "anyhow",
+ "proc-macro-crate 1.3.1",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
+
+[[package]]
+name = "heck"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "html5ever"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
+dependencies = [
+ "log",
+ "mac",
+ "markup5ever",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "http"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa 1.0.15",
+]
+
+[[package]]
+name = "http-range"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "ico"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98"
+dependencies = [
+ "byteorder",
+ "png",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
+
+[[package]]
+name = "icu_properties"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "potential_utf",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
+
+[[package]]
+name = "icu_provider"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "ignore"
+version = "0.4.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
+dependencies = [
+ "crossbeam-deque",
+ "globset",
+ "log",
+ "memchr",
+ "regex-automata 0.4.9",
+ "same-file",
+ "walkdir",
+ "winapi-util",
+]
+
+[[package]]
+name = "image"
+version = "0.24.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "num-traits",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+ "serde",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.15.3",
+ "serde",
+]
+
+[[package]]
+name = "infer"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc"
+dependencies = [
+ "cfb",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "io-kit-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b"
+dependencies = [
+ "core-foundation-sys",
+ "mach2",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
+
+[[package]]
+name = "itoa"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "javascriptcore-rs"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c"
+dependencies = [
+ "bitflags 1.3.2",
+ "glib",
+ "javascriptcore-rs-sys",
+]
+
+[[package]]
+name = "javascriptcore-rs-sys"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 5.0.0",
+]
+
+[[package]]
+name = "jni"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c"
+dependencies = [
+ "cesu8",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror 1.0.69",
+ "walkdir",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "js-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "json-patch"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc"
+dependencies = [
+ "jsonptr",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "jsonptr"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627"
+dependencies = [
+ "fluent-uri",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "kuchikiki"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8"
+dependencies = [
+ "cssparser",
+ "html5ever",
+ "indexmap 1.9.3",
+ "matches",
+ "selectors",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.172"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
+
+[[package]]
+name = "libredox"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags 2.9.1",
+ "libc",
+ "redox_syscall",
+]
+
+[[package]]
+name = "libudev"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0"
+dependencies = [
+ "libc",
+ "libudev-sys",
+]
+
+[[package]]
+name = "libudev-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
+
+[[package]]
+name = "litemap"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+
+[[package]]
+name = "loom"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
+dependencies = [
+ "cfg-if",
+ "generator",
+ "scoped-tls",
+ "serde",
+ "serde_json",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "mac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+
+[[package]]
+name = "mach2"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "markup5ever"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
+dependencies = [
+ "log",
+ "phf 0.10.1",
+ "phf_codegen 0.10.0",
+ "string_cache",
+ "string_cache_codegen",
+ "tendril",
+]
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
+[[package]]
+name = "ndk"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4"
+dependencies = [
+ "bitflags 1.3.2",
+ "jni-sys",
+ "ndk-sys",
+ "num_enum 0.5.11",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
+name = "nix"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "nodrop"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
+dependencies = [
+ "num_enum_derive 0.5.11",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
+dependencies = [
+ "num_enum_derive 0.7.3",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
+dependencies = [
+ "proc-macro-crate 1.3.1",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
+dependencies = [
+ "proc-macro-crate 3.3.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "obdium"
+version = "1.1.0"
+dependencies = [
+ "chrono",
+ "evalexpr",
+ "num_enum 0.7.3",
+ "regex",
+ "serde",
+ "serde_json",
+ "serialport",
+ "sqlite",
+ "tauri",
+ "tauri-build",
+ "thiserror 2.0.12",
+ "tokio",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+ "objc_exception",
+]
+
+[[package]]
+name = "objc_exception"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "objc_id"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
+dependencies = [
+ "objc",
+]
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "open"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8"
+dependencies = [
+ "pathdiff",
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "pango"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f"
+dependencies = [
+ "bitflags 1.3.2",
+ "glib",
+ "libc",
+ "once_cell",
+ "pango-sys",
+]
+
+[[package]]
+name = "pango-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "pathdiff"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "phf"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+dependencies = [
+ "phf_macros 0.8.0",
+ "phf_shared 0.8.0",
+ "proc-macro-hack",
+]
+
+[[package]]
+name = "phf"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
+dependencies = [
+ "phf_shared 0.10.0",
+]
+
+[[package]]
+name = "phf"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
+dependencies = [
+ "phf_macros 0.11.3",
+ "phf_shared 0.11.3",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
+dependencies = [
+ "phf_generator 0.8.0",
+ "phf_shared 0.8.0",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
+dependencies = [
+ "phf_generator 0.10.0",
+ "phf_shared 0.10.0",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+dependencies = [
+ "phf_shared 0.8.0",
+ "rand 0.7.3",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
+dependencies = [
+ "phf_shared 0.10.0",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
+dependencies = [
+ "phf_shared 0.11.3",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
+dependencies = [
+ "phf_generator 0.8.0",
+ "phf_shared 0.8.0",
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
+dependencies = [
+ "phf_generator 0.11.3",
+ "phf_shared 0.11.3",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+dependencies = [
+ "siphasher 0.3.11",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
+dependencies = [
+ "siphasher 0.3.11",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
+dependencies = [
+ "siphasher 1.0.1",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "plist"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d"
+dependencies = [
+ "base64 0.22.1",
+ "indexmap 2.9.0",
+ "quick-xml",
+ "serde",
+ "time",
+]
+
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
+dependencies = [
+ "once_cell",
+ "toml_edit 0.19.15",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
+dependencies = [
+ "toml_edit 0.22.26",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.20+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom 0.1.16",
+ "libc",
+ "rand_chacha 0.2.2",
+ "rand_core 0.5.1",
+ "rand_hc",
+ "rand_pcg",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.16",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_pcg"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
+dependencies = [
+ "bitflags 2.9.1",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
+dependencies = [
+ "getrandom 0.2.16",
+ "libredox",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.9",
+ "regex-syntax 0.8.5",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.5",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
+dependencies = [
+ "bitflags 2.9.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
+
+[[package]]
+name = "ryu"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "selectors"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe"
+dependencies = [
+ "bitflags 1.3.2",
+ "cssparser",
+ "derive_more",
+ "fxhash",
+ "log",
+ "matches",
+ "phf 0.8.0",
+ "phf_codegen 0.8.0",
+ "precomputed-hash",
+ "servo_arc",
+ "smallvec",
+ "thin-slice",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.140"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
+dependencies = [
+ "indexmap 2.9.0",
+ "itoa 1.0.15",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_repr"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_with"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
+dependencies = [
+ "base64 0.22.1",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.9.0",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "serde_with_macros",
+ "time",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "serialize-to-javascript"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5"
+dependencies = [
+ "serde",
+ "serde_json",
+ "serialize-to-javascript-impl",
+]
+
+[[package]]
+name = "serialize-to-javascript-impl"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "serialport"
+version = "4.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb0bc984f6af6ef8bab54e6cf2071579ee75b9286aa9f2319a0d220c28b0a2b"
+dependencies = [
+ "bitflags 2.9.1",
+ "cfg-if",
+ "core-foundation 0.10.0",
+ "core-foundation-sys",
+ "io-kit-sys",
+ "libudev",
+ "mach2",
+ "nix",
+ "scopeguard",
+ "unescaper",
+ "winapi",
+]
+
+[[package]]
+name = "servo_arc"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432"
+dependencies = [
+ "nodrop",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
+[[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
+[[package]]
+name = "siphasher"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
+
+[[package]]
+name = "soup2"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0"
+dependencies = [
+ "bitflags 1.3.2",
+ "gio",
+ "glib",
+ "libc",
+ "once_cell",
+ "soup2-sys",
+]
+
+[[package]]
+name = "soup2-sys"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf"
+dependencies = [
+ "bitflags 1.3.2",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 5.0.0",
+]
+
+[[package]]
+name = "sqlite"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f66e9c01a11936154f3910dbba732c01f8b591543bc4d6672bddee79fd9c4783"
+dependencies = [
+ "sqlite3-sys",
+]
+
+[[package]]
+name = "sqlite3-src"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5b6d3c860886b0a33e69e421796a5f4a27f23597a182c2450f6d7ace5103120"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
+[[package]]
+name = "sqlite3-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7781d97adc13a1d5081127a9ee29afad8427f3757bd984daf814d8265267039"
+dependencies = [
+ "sqlite3-src",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "state"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b"
+dependencies = [
+ "loom",
+]
+
+[[package]]
+name = "string_cache"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f"
+dependencies = [
+ "new_debug_unreachable",
+ "parking_lot",
+ "phf_shared 0.11.3",
+ "precomputed-hash",
+ "serde",
+]
+
+[[package]]
+name = "string_cache_codegen"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0"
+dependencies = [
+ "phf_generator 0.11.3",
+ "phf_shared 0.11.3",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "system-deps"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e"
+dependencies = [
+ "cfg-expr 0.9.1",
+ "heck 0.3.3",
+ "pkg-config",
+ "toml 0.5.11",
+ "version-compare 0.0.11",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+dependencies = [
+ "cfg-expr 0.15.8",
+ "heck 0.5.0",
+ "pkg-config",
+ "toml 0.8.22",
+ "version-compare 0.2.0",
+]
+
+[[package]]
+name = "tao"
+version = "0.16.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48d298c441a1da46e28e8ad8ec205aab7fd8cd71b9d10e05454224eef422e1ae"
+dependencies = [
+ "bitflags 1.3.2",
+ "cairo-rs",
+ "cc",
+ "cocoa",
+ "core-foundation 0.9.4",
+ "core-graphics",
+ "crossbeam-channel",
+ "dispatch",
+ "gdk",
+ "gdk-pixbuf",
+ "gdk-sys",
+ "gdkwayland-sys",
+ "gdkx11-sys",
+ "gio",
+ "glib",
+ "glib-sys",
+ "gtk",
+ "image",
+ "instant",
+ "jni",
+ "lazy_static",
+ "libc",
+ "log",
+ "ndk",
+ "ndk-context",
+ "ndk-sys",
+ "objc",
+ "once_cell",
+ "parking_lot",
+ "png",
+ "raw-window-handle",
+ "scopeguard",
+ "serde",
+ "tao-macros",
+ "unicode-segmentation",
+ "uuid",
+ "windows 0.39.0",
+ "windows-implement 0.39.0",
+ "x11-dl",
+]
+
+[[package]]
+name = "tao-macros"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "tar"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
+dependencies = [
+ "filetime",
+ "libc",
+ "xattr",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+
+[[package]]
+name = "tauri"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ae1f57c291a6ab8e1d2e6b8ad0a35ff769c9925deb8a89de85425ff08762d0c"
+dependencies = [
+ "anyhow",
+ "cocoa",
+ "dirs-next",
+ "dunce",
+ "embed_plist",
+ "encoding_rs",
+ "flate2",
+ "futures-util",
+ "getrandom 0.2.16",
+ "glib",
+ "glob",
+ "gtk",
+ "heck 0.5.0",
+ "http",
+ "ignore",
+ "log",
+ "objc",
+ "once_cell",
+ "open",
+ "percent-encoding",
+ "plist",
+ "rand 0.8.5",
+ "raw-window-handle",
+ "regex",
+ "semver",
+ "serde",
+ "serde_json",
+ "serde_repr",
+ "serialize-to-javascript",
+ "state",
+ "tar",
+ "tauri-macros",
+ "tauri-runtime",
+ "tauri-runtime-wry",
+ "tauri-utils",
+ "tempfile",
+ "thiserror 1.0.69",
+ "tokio",
+ "url",
+ "uuid",
+ "webkit2gtk",
+ "webview2-com",
+ "windows 0.39.0",
+]
+
+[[package]]
+name = "tauri-build"
+version = "1.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2db08694eec06f53625cfc6fff3a363e084e5e9a238166d2989996413c346453"
+dependencies = [
+ "anyhow",
+ "cargo_toml",
+ "dirs-next",
+ "heck 0.5.0",
+ "json-patch",
+ "semver",
+ "serde",
+ "serde_json",
+ "tauri-utils",
+ "tauri-winres",
+ "walkdir",
+]
+
+[[package]]
+name = "tauri-codegen"
+version = "1.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53438d78c4a037ffe5eafa19e447eea599bedfb10844cb08ec53c2471ac3ac3f"
+dependencies = [
+ "base64 0.21.7",
+ "brotli",
+ "ico",
+ "json-patch",
+ "plist",
+ "png",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "semver",
+ "serde",
+ "serde_json",
+ "sha2",
+ "tauri-utils",
+ "thiserror 1.0.69",
+ "time",
+ "uuid",
+ "walkdir",
+]
+
+[[package]]
+name = "tauri-macros"
+version = "1.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233988ac08c1ed3fe794cd65528d48d8f7ed4ab3895ca64cdaa6ad4d00c45c0b"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "tauri-codegen",
+ "tauri-utils",
+]
+
+[[package]]
+name = "tauri-runtime"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8066855882f00172935e3fa7d945126580c34dcbabab43f5d4f0c2398a67d47b"
+dependencies = [
+ "gtk",
+ "http",
+ "http-range",
+ "rand 0.8.5",
+ "raw-window-handle",
+ "serde",
+ "serde_json",
+ "tauri-utils",
+ "thiserror 1.0.69",
+ "url",
+ "uuid",
+ "webview2-com",
+ "windows 0.39.0",
+]
+
+[[package]]
+name = "tauri-runtime-wry"
+version = "0.14.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce361fec1e186705371f1c64ae9dd2a3a6768bc530d0a2d5e75a634bb416ad4d"
+dependencies = [
+ "cocoa",
+ "gtk",
+ "percent-encoding",
+ "rand 0.8.5",
+ "raw-window-handle",
+ "tauri-runtime",
+ "tauri-utils",
+ "uuid",
+ "webkit2gtk",
+ "webview2-com",
+ "windows 0.39.0",
+ "wry",
+]
+
+[[package]]
+name = "tauri-utils"
+version = "1.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c357952645e679de02cd35007190fcbce869b93ffc61b029f33fe02648453774"
+dependencies = [
+ "brotli",
+ "ctor",
+ "dunce",
+ "glob",
+ "heck 0.5.0",
+ "html5ever",
+ "infer",
+ "json-patch",
+ "kuchikiki",
+ "log",
+ "memchr",
+ "phf 0.11.3",
+ "proc-macro2",
+ "quote",
+ "semver",
+ "serde",
+ "serde_json",
+ "serde_with",
+ "thiserror 1.0.69",
+ "url",
+ "walkdir",
+ "windows-version",
+]
+
+[[package]]
+name = "tauri-winres"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb"
+dependencies = [
+ "embed-resource",
+ "toml 0.7.8",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
+dependencies = [
+ "fastrand",
+ "getrandom 0.3.3",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "tendril"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
+dependencies = [
+ "futf",
+ "mac",
+ "utf-8",
+]
+
+[[package]]
+name = "thin-slice"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl 2.0.12",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "time"
+version = "0.3.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
+dependencies = [
+ "deranged",
+ "itoa 1.0.15",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
+
+[[package]]
+name = "time-macros"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tokio"
+version = "1.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit 0.19.15",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit 0.22.26",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
+dependencies = [
+ "indexmap 2.9.0",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow 0.5.40",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
+dependencies = [
+ "indexmap 2.9.0",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_write",
+ "winnow 0.7.10",
+]
+
+[[package]]
+name = "toml_write"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
+
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "typenum"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
+
+[[package]]
+name = "unescaper"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c01d12e3a56a4432a8b436f293c25f4808bdf9e9f9f98f9260bba1f1bc5a1f26"
+dependencies = [
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "url"
+version = "2.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "uuid"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
+dependencies = [
+ "getrandom 0.3.3",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
+name = "version-compare"
+version = "0.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b"
+
+[[package]]
+name = "version-compare"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "vswhom"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b"
+dependencies = [
+ "libc",
+ "vswhom-sys",
+]
+
+[[package]]
+name = "vswhom-sys"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasi"
+version = "0.14.2+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "webkit2gtk"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370"
+dependencies = [
+ "bitflags 1.3.2",
+ "cairo-rs",
+ "gdk",
+ "gdk-sys",
+ "gio",
+ "gio-sys",
+ "glib",
+ "glib-sys",
+ "gobject-sys",
+ "gtk",
+ "gtk-sys",
+ "javascriptcore-rs",
+ "libc",
+ "once_cell",
+ "soup2",
+ "webkit2gtk-sys",
+]
+
+[[package]]
+name = "webkit2gtk-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3"
+dependencies = [
+ "atk-sys",
+ "bitflags 1.3.2",
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gdk-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "gtk-sys",
+ "javascriptcore-rs-sys",
+ "libc",
+ "pango-sys",
+ "pkg-config",
+ "soup2-sys",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "webview2-com"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178"
+dependencies = [
+ "webview2-com-macros",
+ "webview2-com-sys",
+ "windows 0.39.0",
+ "windows-implement 0.39.0",
+]
+
+[[package]]
+name = "webview2-com-macros"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "webview2-com-sys"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7"
+dependencies = [
+ "regex",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+ "windows 0.39.0",
+ "windows-bindgen",
+ "windows-metadata",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a"
+dependencies = [
+ "windows-implement 0.39.0",
+ "windows_aarch64_msvc 0.39.0",
+ "windows_i686_gnu 0.39.0",
+ "windows_i686_msvc 0.39.0",
+ "windows_x86_64_gnu 0.39.0",
+ "windows_x86_64_msvc 0.39.0",
+]
+
+[[package]]
+name = "windows"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-bindgen"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41"
+dependencies = [
+ "windows-metadata",
+ "windows-tokens",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
+dependencies = [
+ "windows-implement 0.60.0",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7"
+dependencies = [
+ "syn 1.0.109",
+ "windows-tokens",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+
+[[package]]
+name = "windows-metadata"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278"
+
+[[package]]
+name = "windows-result"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "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]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-tokens"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597"
+
+[[package]]
+name = "windows-version"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "winnow"
+version = "0.5.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "winnow"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "winreg"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+dependencies = [
+ "bitflags 2.9.1",
+]
+
+[[package]]
+name = "writeable"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
+
+[[package]]
+name = "wry"
+version = "0.24.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c55c80b12287eb1ff7c365fc2f7a5037cb6181bd44c9fce81c8d1cf7605ffad6"
+dependencies = [
+ "base64 0.13.1",
+ "block",
+ "cocoa",
+ "core-graphics",
+ "crossbeam-channel",
+ "dunce",
+ "gdk",
+ "gio",
+ "glib",
+ "gtk",
+ "html5ever",
+ "http",
+ "kuchikiki",
+ "libc",
+ "log",
+ "objc",
+ "objc_id",
+ "once_cell",
+ "serde",
+ "serde_json",
+ "sha2",
+ "soup2",
+ "tao",
+ "thiserror 1.0.69",
+ "url",
+ "webkit2gtk",
+ "webkit2gtk-sys",
+ "webview2-com",
+ "windows 0.39.0",
+ "windows-implement 0.39.0",
+]
+
+[[package]]
+name = "x11"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "x11-dl"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
+dependencies = [
+ "libc",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "xattr"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e"
+dependencies = [
+ "libc",
+ "rustix",
+]
+
+[[package]]
+name = "yoke"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+ "synstructure",
+]
+
+[[package]]
+name = "zerotrie"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
diff --git a/Cargo.toml b/backend/Cargo.toml
similarity index 56%
rename from Cargo.toml
rename to backend/Cargo.toml
index d0ed118..abe54ad 100644
--- a/Cargo.toml
+++ b/backend/Cargo.toml
@@ -1,18 +1,23 @@
[package]
name = "obdium"
-version = "0.1.0"
+version = "1.1.0"
edition = "2021"
[dependencies]
serialport = "4.7.1"
sqlite = "0.37.0"
chrono = "0.4.41"
-
num_enum = "0.7.3"
thiserror = "2.0.12"
-
regex = "1.11.1"
evalexpr = "12.0.2"
-
serde_json = "1.0.140"
serde = { version = "^1.0.190", features = ["derive"] }
+tokio = "1.45.0"
+tauri = { version = "1", features = ["shell-open"] }
+
+[build-dependencies]
+tauri-build = { version = "1", features = [] }
+
+[features]
+custom-protocol = ["tauri/custom-protocol"]
diff --git a/backend/build.rs b/backend/build.rs
new file mode 100644
index 0000000..d860e1e
--- /dev/null
+++ b/backend/build.rs
@@ -0,0 +1,3 @@
+fn main() {
+ tauri_build::build()
+}
diff --git a/data/code-descriptions.sqlite b/backend/data/code-descriptions.sqlite
similarity index 100%
rename from data/code-descriptions.sqlite
rename to backend/data/code-descriptions.sqlite
diff --git a/data/model-pids.sqlite b/backend/data/model-pids.sqlite
similarity index 100%
rename from data/model-pids.sqlite
rename to backend/data/model-pids.sqlite
diff --git a/backend/data/requests.json b/backend/data/requests.json
new file mode 100644
index 0000000..61045e2
--- /dev/null
+++ b/backend/data/requests.json
@@ -0,0 +1,404 @@
+[
+ {
+ "request": "010C",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 0C 23 82 \n\n",
+ "played": true
+ },
+ {
+ "request": "0174",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010D",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0D 72 \n\n",
+ "played": true
+ },
+ {
+ "request": "010C",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 0C 26 25 \n\n",
+ "played": true
+ },
+ {
+ "request": "0174",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010D",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0D 48 \n\n",
+ "played": true
+ },
+ {
+ "request": "0104",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 04 56 \n\n",
+ "played": true
+ },
+ {
+ "request": "0163",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010A",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "0122",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "0123",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 23 1A 0E \n\n",
+ "played": true
+ },
+ {
+ "request": "010C",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 0C 18 9F \n\n",
+ "played": true
+ },
+ {
+ "request": "0174",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010D",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0D 20 \n\n",
+ "played": true
+ },
+ {
+ "request": "010C",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 0C 13 FB \n\n",
+ "played": true
+ },
+ {
+ "request": "0174",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010D",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0D 29 \n\n",
+ "played": true
+ },
+ {
+ "request": "012E",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 2E 00 \n\n",
+ "played": true
+ },
+ {
+ "request": "0132",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "0142",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 42 39 5F \n\n",
+ "played": true
+ },
+ {
+ "request": "0101",
+ "request_type": "PIDCommand",
+ "response": "7E8 06 41 01 00 07 A1 00 \n\n",
+ "played": true
+ },
+ {
+ "request": "011F",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 1F 00 00 \n\n",
+ "played": true
+ },
+ {
+ "request": "01A2",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "0150",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010E",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0E A6 \n\n",
+ "played": true
+ },
+ {
+ "request": "010B",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0B 26 \n\n",
+ "played": true
+ },
+ {
+ "request": "0133",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 33 61 \n\n",
+ "played": true
+ },
+ {
+ "request": "0118",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "0119",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "011A",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "011B",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010C",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 0C 3F 7A \n\n",
+ "played": true
+ },
+ {
+ "request": "0174",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010D",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0D 0E \n\n",
+ "played": true
+ },
+ {
+ "request": "010C",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 0C 14 5F \n\n",
+ "played": true
+ },
+ {
+ "request": "0174",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010D",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0D 0A \n\n",
+ "played": true
+ },
+ {
+ "request": "0111",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 11 2B \n\n",
+ "played": true
+ },
+ {
+ "request": "0145",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 45 42 \n\n",
+ "played": true
+ },
+ {
+ "request": "0147",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 47 E9 \n\n",
+ "played": true
+ },
+ {
+ "request": "0148",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "0149",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 49 00 \n\n",
+ "played": true
+ },
+ {
+ "request": "010C",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 0C 3B 2E \n\n",
+ "played": true
+ },
+ {
+ "request": "0174",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010D",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0D 20 \n\n",
+ "played": true
+ },
+ {
+ "request": "0104",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 04 69 \n\n",
+ "played": true
+ },
+ {
+ "request": "0163",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010A",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "0122",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "0123",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 23 1A 0E \n\n",
+ "played": true
+ },
+ {
+ "request": "010C",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 0C 15 2A \n\n",
+ "played": true
+ },
+ {
+ "request": "0174",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010D",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0D 29 \n\n",
+ "played": true
+ },
+ {
+ "request": "010C",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 0C 3F 7A \n\n",
+ "played": true
+ },
+ {
+ "request": "0174",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010D",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0D 00 \n\n",
+ "played": true
+ },
+ {
+ "request": "010C",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 0C 14 5F \n\n",
+ "played": true
+ },
+ {
+ "request": "0174",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010D",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0D 44 \n\n",
+ "played": true
+ },
+ {
+ "request": "0110",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 10 18 1F \n\n",
+ "played": true
+ },
+ {
+ "request": "019D",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "221470",
+ "request_type": "Arbitrary",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "0161",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "0162",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010C",
+ "request_type": "PIDCommand",
+ "response": "7E8 04 41 0C 00 00 \n\n",
+ "played": true
+ },
+ {
+ "request": "0174",
+ "request_type": "PIDCommand",
+ "response": "NO DATA",
+ "played": true
+ },
+ {
+ "request": "010D",
+ "request_type": "PIDCommand",
+ "response": "7E8 03 41 0D 5C \n\n",
+ "played": true
+ }
+]
\ No newline at end of file
diff --git a/data/vpic.sqlite b/backend/data/vpic.sqlite
similarity index 100%
rename from data/vpic.sqlite
rename to backend/data/vpic.sqlite
diff --git a/backend/icons/128x128.png b/backend/icons/128x128.png
new file mode 100644
index 0000000..6be5e50
Binary files /dev/null and b/backend/icons/128x128.png differ
diff --git a/backend/icons/128x128@2x.png b/backend/icons/128x128@2x.png
new file mode 100644
index 0000000..e81bece
Binary files /dev/null and b/backend/icons/128x128@2x.png differ
diff --git a/backend/icons/32x32.png b/backend/icons/32x32.png
new file mode 100644
index 0000000..a437dd5
Binary files /dev/null and b/backend/icons/32x32.png differ
diff --git a/backend/icons/Square107x107Logo.png b/backend/icons/Square107x107Logo.png
new file mode 100644
index 0000000..0ca4f27
Binary files /dev/null and b/backend/icons/Square107x107Logo.png differ
diff --git a/backend/icons/Square142x142Logo.png b/backend/icons/Square142x142Logo.png
new file mode 100644
index 0000000..b81f820
Binary files /dev/null and b/backend/icons/Square142x142Logo.png differ
diff --git a/backend/icons/Square150x150Logo.png b/backend/icons/Square150x150Logo.png
new file mode 100644
index 0000000..624c7bf
Binary files /dev/null and b/backend/icons/Square150x150Logo.png differ
diff --git a/backend/icons/Square284x284Logo.png b/backend/icons/Square284x284Logo.png
new file mode 100644
index 0000000..c021d2b
Binary files /dev/null and b/backend/icons/Square284x284Logo.png differ
diff --git a/backend/icons/Square30x30Logo.png b/backend/icons/Square30x30Logo.png
new file mode 100644
index 0000000..6219700
Binary files /dev/null and b/backend/icons/Square30x30Logo.png differ
diff --git a/backend/icons/Square310x310Logo.png b/backend/icons/Square310x310Logo.png
new file mode 100644
index 0000000..f9bc048
Binary files /dev/null and b/backend/icons/Square310x310Logo.png differ
diff --git a/backend/icons/Square44x44Logo.png b/backend/icons/Square44x44Logo.png
new file mode 100644
index 0000000..d5fbfb2
Binary files /dev/null and b/backend/icons/Square44x44Logo.png differ
diff --git a/backend/icons/Square71x71Logo.png b/backend/icons/Square71x71Logo.png
new file mode 100644
index 0000000..63440d7
Binary files /dev/null and b/backend/icons/Square71x71Logo.png differ
diff --git a/backend/icons/Square89x89Logo.png b/backend/icons/Square89x89Logo.png
new file mode 100644
index 0000000..f3f705a
Binary files /dev/null and b/backend/icons/Square89x89Logo.png differ
diff --git a/backend/icons/StoreLogo.png b/backend/icons/StoreLogo.png
new file mode 100644
index 0000000..4556388
Binary files /dev/null and b/backend/icons/StoreLogo.png differ
diff --git a/backend/icons/icon.icns b/backend/icons/icon.icns
new file mode 100644
index 0000000..12a5bce
Binary files /dev/null and b/backend/icons/icon.icns differ
diff --git a/backend/icons/icon.ico b/backend/icons/icon.ico
new file mode 100644
index 0000000..4025c3d
Binary files /dev/null and b/backend/icons/icon.ico differ
diff --git a/backend/icons/icon.png b/backend/icons/icon.png
new file mode 100644
index 0000000..e1cd261
Binary files /dev/null and b/backend/icons/icon.png differ
diff --git a/backend/src/bridge/events.rs b/backend/src/bridge/events.rs
new file mode 100644
index 0000000..1f7ad45
--- /dev/null
+++ b/backend/src/bridge/events.rs
@@ -0,0 +1,268 @@
+use super::{ConnectPaylod, ConnectionStatus, Setting, VehicleInfo, VehicleInfoExtended};
+/// Events to bridge the frontend with
+/// the backend.
+///
+/// Includes listening for requests from the frontend
+/// to perform actions
+use crate::{connect_obd, track_data, OBD};
+use obdium::dicts::PID_INFOS;
+use obdium::vin::VIN;
+use std::sync::{Arc, Mutex};
+use std::time::Duration;
+use tauri::{async_runtime::spawn, Window};
+use tokio::time::sleep;
+
+// "Listen" events
+//
+// Where the backend "listens" for events from the frontend
+// All listen events are prefixed with 'listen'
+
+pub fn listen_send_pids(window: &Arc) {
+ let window_arc = Arc::new(window.clone());
+ let window_clone = Arc::clone(&window_arc);
+ window_clone.listen("get-pids", move |_| {
+ let _ = window_arc.emit("update-pids", PID_INFOS);
+ });
+}
+
+pub fn listen_send_ports(window: &Arc) {
+ let window_arc = Arc::new(window.clone());
+ let window_clone = Arc::clone(&window_arc);
+ window_clone.listen("get-serial-ports", move |_| {
+ let _ = window_arc.emit("update-serial-ports", OBD::get_open_serial_port());
+ });
+}
+
+// TODO: This is an eye sore.
+pub fn listen_decode_vin(window: &Window) {
+ let window_arc = Arc::new(window.clone());
+ let window_clone = Arc::clone(&window_arc);
+ window_clone.listen("decode-vin", move |event| {
+ let vin = match VIN::new(event.payload().unwrap().replace("\"", "")) {
+ Ok(vin) => vin,
+ Err(err) => {
+ // emit error
+ let v_info = VehicleInfoExtended {
+ error_msg: format!("{}", err),
+ ..Default::default()
+ };
+
+ let _ = window_arc.emit("decode-vin", v_info);
+ return;
+ }
+ };
+
+ let v_info = VehicleInfoExtended {
+ error_msg: "Decoded successfully.".to_string(),
+ vin: vin.get_vin().to_string(),
+ make: vin.get_vehicle_make().unwrap_or("N/A".into()),
+ model: vin.get_vehicle_model().unwrap_or("N/A".into()),
+ model_year: vin.get_model_year().unwrap_or(-1).to_string(),
+ engine_model: vin.get_engine_model().unwrap_or("N/A".into()),
+ cylinder_count: vin.get_cylinder_count().unwrap_or(-1).to_string(),
+ axel_count: vin.get_axle_count().unwrap_or(-1).to_string(),
+ traction_control: vin.traction_control().unwrap_or("N/A".into()),
+ plant_country: vin.get_plant_country().unwrap_or("N/A".into()),
+ plant_city: vin.get_plant_city().unwrap_or("N/A".into()),
+ plant_state: vin.get_plant_state().unwrap_or("N/A".into()),
+ semi_auto_headlamp_beam_switching: vin
+ .semiauto_headlamp_beam_switching()
+ .unwrap_or("N/A".into()),
+ dynamic_brake_support: vin.dynamic_brake_support().unwrap_or("N/A".into()),
+ airbag_locations_knee: vin.airbag_locations_knee().unwrap_or("N/A".into()),
+ airbag_locations_side: vin.airbag_locations_side().unwrap_or("N/A".into()),
+ body_style: vin.get_body_class().unwrap_or("N/A".into()),
+ fuel_type: vin.get_fuel_type().unwrap_or("N/A".into()),
+ fuel_delivery_type: vin.get_fuel_delivery_type().unwrap_or("N/A".into()),
+ engine_manufacturer: vin.get_engine_manufacturer().unwrap_or("N/A".into()),
+ anti_lock_braking_system: vin.abs_availablility().unwrap_or("N/A".into()),
+ transmission_style: vin.get_transmission_style().unwrap_or("N/A".into()),
+ steering_location: vin.get_steering_location().unwrap_or("N/A".into()),
+ keyless_ignition: vin.keyless_ignition_availability().unwrap_or("N/A".into()),
+ top_speed: vin.get_vehicle_top_speed().unwrap_or(-1).to_string(),
+ daytime_running_light: vin.daytime_running_light().unwrap_or("N/A".into()),
+ window_auto_reverse: vin.windows_auto_reverse().unwrap_or("N/A".into()),
+ airbag_locations_front: vin.airbag_locations_front().unwrap_or("N/A".into()),
+ front_wheel_size: vin.get_front_wheel_size().unwrap_or(-1).to_string(),
+ rear_wheel_size: vin.get_rear_wheel_size().unwrap_or(-1).to_string(),
+ automatic_crash_notification: vin
+ .automatic_crash_notification()
+ .unwrap_or("N/A".into()),
+ trim: vin.vehicle_trim().unwrap_or("N/A".into()),
+ transmission_speeds: vin.get_transmission_speeds().unwrap_or(-1).to_string(),
+ vehicle_base_price: vin.get_vehicle_base_price().unwrap_or(-1.0).to_string(),
+ number_of_rows: vin.number_of_rows().unwrap_or(-1).to_string(),
+ number_of_seats: vin.number_of_seats().unwrap_or(-1).to_string(),
+ brake_system: vin.get_brake_system().unwrap_or("N/A".into()),
+ engine_displacement: vin.get_engine_displacement().unwrap_or(-1.0).to_string(),
+ gross_vehicle_weight_rating: vin.get_vehicle_weight_rating().unwrap_or("N/A".into()),
+ airbag_locations_curtain: vin.airbag_locations_curtain().unwrap_or("N/A".into()),
+ backup_camera: vin.backup_camera().unwrap_or("N/A".into()),
+ drive_type: vin.get_drive_type().unwrap_or("N/A".into()),
+ vehicle_manufacturer: vin.get_vehicle_manufacturer().unwrap_or("N/A".into()),
+ };
+
+ let _ = window_arc.emit("decode-vin", v_info);
+ });
+}
+
+pub fn listen_connect_elm(window: &Arc) {
+ let window_arc = Arc::new(window.clone());
+ let window_clone = Arc::clone(&window_arc);
+ window_clone.listen("connect-elm", move |event| {
+ let payload = event.payload().unwrap_or("");
+ let connect_payload: ConnectPaylod = match serde_json::from_str(payload) {
+ Ok(p) => p,
+ Err(e) => {
+ println!("Failed to parse connect payload: {e}");
+ return;
+ }
+ };
+
+ let obd = connect_obd(
+ &window_arc,
+ connect_payload.serial_port,
+ connect_payload.baud_rate,
+ connect_payload.protocol,
+ );
+
+ if let Some(obd) = obd {
+ // Arc's
+ let obd = Arc::new(Mutex::new(obd));
+
+ // Usually called once
+ do_send_vehicle_details(&window_arc, &obd);
+
+ // Live tracking data
+ track_data(&window_arc, &obd);
+
+ // spawn thread to keep checking if obd disconnects
+ let window_arc_clone = Arc::clone(&window_arc);
+ let obd_clone = Arc::clone(&obd);
+ spawn(async move {
+ loop {
+ sleep(Duration::from_secs(1)).await;
+ let obd = obd_clone.lock().unwrap();
+ if !obd.connected() {
+ do_send_connection_status(
+ &window_arc_clone,
+ &obd,
+ "Connection dropped".to_string(),
+ false,
+ );
+ break;
+ }
+ }
+ });
+
+ // Listen for disconnect-elm event outside of async block to avoid lifetime issues
+ let window_arc_for_listen = Arc::clone(&window_arc);
+ let obd_for_listen = Arc::clone(&obd);
+ window_arc_for_listen.listen("disconnect-elm", {
+ let window_arc_for_listen = Arc::clone(&window_arc_for_listen);
+ move |_| {
+ let mut obd = obd_for_listen.lock().unwrap();
+ obd.disconnect();
+ do_send_connection_status(
+ &window_arc_for_listen,
+ &obd,
+ "Connection dropped".to_string(),
+ false,
+ );
+ }
+ });
+
+ listen_change_obd_settings(&window_arc, &obd);
+ }
+ });
+}
+
+pub fn listen_change_obd_settings(window: &Arc, obd: &Arc>) {
+ let obd_arc = Arc::clone(obd);
+ window.listen("settings-changed", move |event| {
+ let payload = match event.payload() {
+ Some(payload) => payload,
+ None => return,
+ };
+
+ let setting: Setting = match serde_json::from_str(payload) {
+ Ok(p) => p,
+ Err(e) => {
+ println!("Failed to parse setting payload: {e}");
+ return;
+ }
+ };
+
+ let mut obd = obd_arc.lock().unwrap();
+ match setting.t_id.as_str() {
+ "record-responses" => obd.record_requests(setting.checked),
+ "replay-responses" => obd.replay_requests(setting.checked),
+ "use-freeze-fram" => obd.query_freeze_frame(setting.checked),
+ _ => (),
+ }
+ });
+}
+
+// Calls to the frontend from the backend
+// where the backend umprompty tells the
+// frontend to do something.
+//
+// In this case, the frontend would be listening for these events.
+
+pub fn do_send_vehicle_details(window: &Arc, obd: &Arc>) {
+ let obd = Arc::clone(obd);
+ let window = Arc::clone(window);
+ spawn(async move {
+ let mut obd = obd.lock().unwrap();
+
+ // send the vin and vehicle details to the frontend
+ match obd.get_vin() {
+ Some(vin) => {
+ let make = match vin.get_vehicle_make() {
+ Ok(make) => make,
+ Err(err) => {
+ println!("failed to resolve vehicle make from vin: {}", vin.get_vin());
+ println!("error: {err}");
+ "??".to_string()
+ }
+ };
+
+ let model = match vin.get_vehicle_model() {
+ Ok(model) => model,
+ Err(err) => {
+ println!(
+ "failed to resolve vehicle model from vin: {}",
+ vin.get_vin()
+ );
+ println!("error: {err}");
+ "??".to_string()
+ }
+ };
+
+ let v_info = VehicleInfo {
+ vin: vin.get_vin().to_string(),
+ make,
+ model,
+ };
+
+ window.emit("vehicle-details", v_info).unwrap();
+ }
+ None => {
+ println!("error: getting vin. vin is none.");
+ }
+ };
+ });
+}
+
+pub fn do_send_connection_status(window: &Window, obd: &OBD, message: String, connected: bool) {
+ let port = obd.serial_port_name().unwrap_or_default();
+ let baud = obd.serial_port_baud_rate().unwrap_or_default();
+ let conn_status = ConnectionStatus {
+ connected,
+ message,
+ serial_port: port,
+ baud_rate: baud,
+ };
+
+ let _ = window.emit("connection-status", conn_status);
+}
diff --git a/backend/src/bridge/mod.rs b/backend/src/bridge/mod.rs
new file mode 100644
index 0000000..c3e9406
--- /dev/null
+++ b/backend/src/bridge/mod.rs
@@ -0,0 +1,93 @@
+pub mod events;
+
+/// Structs that are used as payloads
+/// between frontend and backend.
+use serde::{Deserialize, Serialize};
+
+/// Very brief vehicle information
+/// No detailed information- use VehicleInfoExtended
+/// with the VIN struct for that instead.
+#[derive(Serialize, Deserialize, Clone)]
+struct VehicleInfo {
+ vin: String,
+ make: String,
+ model: String,
+}
+
+/// All relevant information about a
+/// vehicle.
+///
+/// Essential when the frontend
+/// requests vehicle info about a VIN but
+/// can't use the VIN struct and it's methods.
+#[derive(Serialize, Deserialize, Clone, Default)]
+struct VehicleInfoExtended {
+ error_msg: String,
+ vin: String,
+ make: String,
+ model: String,
+ model_year: String,
+ engine_model: String,
+ cylinder_count: String,
+ axel_count: String,
+ traction_control: String,
+ plant_country: String,
+ plant_city: String,
+ plant_state: String,
+ semi_auto_headlamp_beam_switching: String,
+ dynamic_brake_support: String,
+ airbag_locations_knee: String,
+ airbag_locations_side: String,
+ drive_type: String,
+ fuel_type: String,
+ fuel_delivery_type: String,
+ engine_manufacturer: String,
+ anti_lock_braking_system: String,
+ transmission_style: String,
+ steering_location: String,
+ keyless_ignition: String,
+ top_speed: String,
+ daytime_running_light: String,
+ window_auto_reverse: String,
+ airbag_locations_front: String,
+ front_wheel_size: String,
+ rear_wheel_size: String,
+ automatic_crash_notification: String,
+ trim: String,
+ transmission_speeds: String,
+ vehicle_base_price: String,
+ number_of_rows: String,
+ number_of_seats: String,
+ brake_system: String,
+ engine_displacement: String,
+ gross_vehicle_weight_rating: String,
+ airbag_locations_curtain: String,
+ backup_camera: String,
+ body_style: String,
+ vehicle_manufacturer: String,
+}
+
+/// Tells the frontend the serial port
+/// connection status.
+#[derive(Serialize, Deserialize, Clone)]
+struct ConnectionStatus {
+ connected: bool,
+ message: String,
+ serial_port: String,
+ baud_rate: u32,
+}
+
+#[derive(Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct ConnectPaylod {
+ serial_port: String,
+ baud_rate: u32,
+ protocol: u8,
+}
+
+#[derive(Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct Setting {
+ t_id: String,
+ checked: bool,
+}
diff --git a/src/cmd.rs b/backend/src/cmd.rs
similarity index 100%
rename from src/cmd.rs
rename to backend/src/cmd.rs
diff --git a/backend/src/dicts.rs b/backend/src/dicts.rs
new file mode 100644
index 0000000..b7ef9db
--- /dev/null
+++ b/backend/src/dicts.rs
@@ -0,0 +1,169 @@
+use serde::{Deserialize, Serialize};
+
+#[derive(Deserialize, Serialize, Clone)]
+pub struct PidInfo {
+ pid: &'static str,
+ mode: &'static str,
+ unit: &'static str,
+ pid_name: &'static str,
+ formula: &'static str,
+}
+
+pub const PID_INFOS: &[PidInfo] = &[
+ PidInfo { pid: "01", mode: "01", unit: "", pid_name: "Monitor status since DTCs cleared", formula: "", },
+ PidInfo { pid: "02", mode: "01", unit: "", pid_name: "DTC that caused freeze frame to be stored", formula: "", },
+ PidInfo { pid: "03", mode: "01", unit: "", pid_name: "Fuel system status", formula: "", },
+ PidInfo { pid: "04", mode: "01", unit: "%", pid_name: "Calculated engine load", formula: "100/255 * A", },
+ PidInfo { pid: "05", mode: "01", unit: "°C", pid_name: "Engine coolant temperature", formula: "A - 40", },
+ PidInfo { pid: "06", mode: "01", unit: "%", pid_name: "Short term fuel trim Bank 1", formula: "(100/128 * A) - 100", },
+ PidInfo { pid: "07", mode: "01", unit: "%", pid_name: "Long term fuel trim Bank 1", formula: "(100/128 * A) - 100", },
+ PidInfo { pid: "08", mode: "01", unit: "%", pid_name: "Short term fuel trim Bank 2", formula: "(100/128 * A) - 100", },
+ PidInfo { pid: "09", mode: "01", unit: "%", pid_name: "Long term fuel trim Bank 2", formula: "(100/128 * A) - 100", },
+ PidInfo { pid: "0A", mode: "01", unit: "kPa", pid_name: "Fuel pressure", formula: "3 * A", },
+ PidInfo { pid: "0B", mode: "01", unit: "kPa", pid_name: "Intake manifold absolute pressure", formula: "A", },
+ PidInfo { pid: "0C", mode: "01", unit: "rpm", pid_name: "Engine speed", formula: "((256 * A)+B) / 4", },
+ PidInfo { pid: "0D", mode: "01", unit: "km/h", pid_name: "Vehicle speed", formula: "A", },
+ PidInfo { pid: "0E", mode: "01", unit: "", pid_name: "Timing advance", formula: "A/2 - 64", },
+ PidInfo { pid: "0F", mode: "01", unit: "°C", pid_name: "Intake air temperature", formula: "A - 40", },
+ PidInfo { pid: "10", mode: "01", unit: "g/s", pid_name: "MAF air flow rate", formula: "((256 * A)+B) / 100", },
+ PidInfo { pid: "11", mode: "01", unit: "%", pid_name: "Throttle position", formula: "100/255 * A", },
+ PidInfo { pid: "12", mode: "01", unit: "", pid_name: "Commanded secondary air status", formula: "", },
+ PidInfo { pid: "13", mode: "01", unit: "", pid_name: "Oxygen sensors present (in 2 banks)", formula: "", },
+ PidInfo { pid: "14", mode: "01", unit: "(V, %)", pid_name: "Oxygen Sensor 1 (A: Voltage B: STFT)", formula: "V: A / 200 %: 100/128B - 100", },
+ PidInfo { pid: "15", mode: "01", unit: "(V, %)", pid_name: "Oxygen Sensor 2 (A: Voltage B: STFT)", formula: "V: A / 200 %: 100/128B - 100", },
+ PidInfo { pid: "16", mode: "01", unit: "(V, %)", pid_name: "Oxygen Sensor 3 (A: Voltage B: STFT)", formula: "V: A / 200 %: 100/128B - 100", },
+ PidInfo { pid: "17", mode: "01", unit: "(V, %)", pid_name: "Oxygen Sensor 4 (A: Voltage B: STFT)", formula: "V: A / 200 %: 100/128B - 100", },
+ PidInfo { pid: "18", mode: "01", unit: "(V, %)", pid_name: "Oxygen Sensor 5 (A: Voltage B: STFT)", formula: "V: A / 200 %: 100/128B - 100", },
+ PidInfo { pid: "19", mode: "01", unit: "(V, %)", pid_name: "Oxygen Sensor 6 (A: Voltage B: STFT)", formula: "V: A / 200 %: 100/128B - 100", },
+ PidInfo { pid: "1A", mode: "01", unit: "(V, %)", pid_name: "Oxygen Sensor 7 (A: Voltage B: STFT)", formula: "V: A / 200 %: 100/128B - 100", },
+ PidInfo { pid: "1B", mode: "01", unit: "(V, %)", pid_name: "Oxygen Sensor 8 (A: Voltage B: STFT)", formula: "V: A / 200 %: 100/128B - 100", },
+ PidInfo { pid: "1C", mode: "01", unit: "", pid_name: "OBD standards this vehicle conforms to", formula: "", },
+ PidInfo { pid: "1D", mode: "01", unit: "", pid_name: "Oxygen sensors present (in 4 banks)", formula: "", },
+ PidInfo { pid: "1E", mode: "01", unit: "", pid_name: "Aux input status", formula: "", },
+ PidInfo { pid: "1F", mode: "01", unit: "s", pid_name: "Run time since engine start", formula: "(256 * A) + B", },
+ PidInfo { pid: "21", mode: "01", unit: "km", pid_name: "Dist. with (MIL) on", formula: "(256 * A) + B", },
+ PidInfo { pid: "22", mode: "01", unit: "kPa", pid_name: "Fuel Rail Pressure", formula: "0.079(256A + B)", },
+ PidInfo { pid: "23", mode: "01", unit: "kPa", pid_name: "Fuel Rail Gauge Pressure", formula: "10(256A + B)", },
+ PidInfo { pid: "24", mode: "01", unit: "(ratio, V)", pid_name: "Oxygen Sensor 1 (AB: AFR CD: Voltage)", formula: "(ratio: 2/65536(256A+B) V: 8/65536(256C+D)", },
+ PidInfo { pid: "25", mode: "01", unit: "(ratio, V)", pid_name: "Oxygen Sensor 2 (AB: AFR CD: Voltage)", formula: "(ratio: 2/65536(256A+B) V: 8/65536(256C+D)", },
+ PidInfo { pid: "26", mode: "01", unit: "(ratio, V)", pid_name: "Oxygen Sensor 3 (AB: AFR CD: Voltage)", formula: "(ratio: 2/65536(256A+B) V: 8/65536(256C+D)", },
+ PidInfo { pid: "27", mode: "01", unit: "(ratio, V)", pid_name: "Oxygen Sensor 4 (AB: AFR CD: Voltage)", formula: "(ratio: 2/65536(256A+B) V: 8/65536(256C+D)", },
+ PidInfo { pid: "28", mode: "01", unit: "(ratio, V)", pid_name: "Oxygen Sensor 5 (AB: AFR CD: Voltage)", formula: "(ratio: 2/65536(256A+B) V: 8/65536(256C+D)", },
+ PidInfo { pid: "29", mode: "01", unit: "(ratio, V)", pid_name: "Oxygen Sensor 6 (AB: AFR CD: Voltage)", formula: "(ratio: 2/65536(256A+B) V: 8/65536(256C+D)", },
+ PidInfo { pid: "2A", mode: "01", unit: "(ratio, V)", pid_name: "Oxygen Sensor 7 (AB: AFR CD: Voltage)", formula: "(ratio: 2/65536(256A+B) V: 8/65536(256C+D)", },
+ PidInfo { pid: "2B", mode: "01", unit: "(ratio, V)", pid_name: "Oxygen Sensor 8 (AB: AFR CD: Voltage)", formula: "(ratio: 2/65536(256A+B) V: 8/65536(256C+D)", },
+ PidInfo { pid: "2C", mode: "01", unit: "%", pid_name: "Commanded EGR", formula: "100/255 * A", },
+ PidInfo { pid: "2D", mode: "01", unit: "%", pid_name: "EGR Error", formula: "(100/128 * A) - 100", },
+ PidInfo { pid: "2E", mode: "01", unit: "%", pid_name: "Commanded evaporative purge", formula: "100/255 * A", },
+ PidInfo { pid: "2F", mode: "01", unit: "%", pid_name: "Fuel Tank Level Input", formula: "100/255 * A", },
+ PidInfo { pid: "30", mode: "01", unit: "", pid_name: "Warm-ups since codes cleared", formula: "A", },
+ PidInfo { pid: "31", mode: "01", unit: "km", pid_name: "Distance traveled since codes cleared", formula: "(256 * A)+B", },
+ PidInfo { pid: "32", mode: "01", unit: "Pa", pid_name: "Evap. System Vapor Pressure", formula: "((256 * A)+B) / 4", },
+ PidInfo { pid: "33", mode: "01", unit: "kPa", pid_name: "Absolute Barometric Pressure", formula: "A", },
+ PidInfo { pid: "34", mode: "01", unit: "(ratio, mA)", pid_name: "Oxygen Sensor 1 (AB: AFR CD: Current)", formula: "(ratio: 2/65536(256A+B) mA: ((256C + D) / 256) - 128", },
+ PidInfo { pid: "35", mode: "01", unit: "(ratio, mA)", pid_name: "Oxygen Sensor 2 (AB: AFR CD: Current)", formula: "(ratio: 2/65536(256A+B) mA: ((256C + D) / 256) - 128", },
+ PidInfo { pid: "36", mode: "01", unit: "(ratio, mA)", pid_name: "Oxygen Sensor 3 (AB: AFR CD: Current)", formula: "(ratio: 2/65536(256A+B) mA: ((256C + D) / 256) - 128", },
+ PidInfo { pid: "37", mode: "01", unit: "(ratio, mA)", pid_name: "Oxygen Sensor 4 (AB: AFR CD: Current)", formula: "(ratio: 2/65536(256A+B) mA: ((256C + D) / 256) - 128", },
+ PidInfo { pid: "38", mode: "01", unit: "(ratio, mA)", pid_name: "Oxygen Sensor 5 (AB: AFR CD: Current)", formula: "(ratio: 2/65536(256A+B) mA: ((256C + D) / 256) - 128", },
+ PidInfo { pid: "39", mode: "01", unit: "(ratio, mA)", pid_name: "Oxygen Sensor 6 (AB: AFR CD: Current)", formula: "(ratio: 2/65536(256A+B) mA: ((256C + D) / 256) - 128", },
+ PidInfo { pid: "3A", mode: "01", unit: "(ratio, mA)", pid_name: "Oxygen Sensor 7 (AB: AFR CD: Current)", formula: "(ratio: 2/65536(256A+B) mA: ((256C + D) / 256) - 128", },
+ PidInfo { pid: "3B", mode: "01", unit: "(ratio, mA)", pid_name: "Oxygen Sensor 8 (AB: AFR CD: Current)", formula: "(ratio: 2/65536(256A+B) mA: ((256C + D) / 256) - 128", },
+ PidInfo { pid: "3C", mode: "01", unit: "°C", pid_name: "Catalyst Temperature: Bank 1, Sensor 1", formula: "(((256 * A)+B) / 10) - 40", },
+ PidInfo { pid: "3D", mode: "01", unit: "°C", pid_name: "Catalyst Temperature: Bank 2, Sensor 1", formula: "(((256 * A)+B) / 10) - 40", },
+ PidInfo { pid: "3E", mode: "01", unit: "°C", pid_name: "Catalyst Temperature: Bank 1, Sensor 2", formula: "(((256 * A)+B) / 10) - 40", },
+ PidInfo { pid: "3F", mode: "01", unit: "°C", pid_name: "Catalyst Temperature: Bank 2, Sensor 2", formula: "(((256 * A)+B) / 10) - 40", },
+ PidInfo { pid: "41", mode: "01", unit: "", pid_name: "Monitor status this drive cycle", formula: "", },
+ PidInfo { pid: "42", mode: "01", unit: "V", pid_name: "Control module voltage", formula: "((256 * A)+B) / 1000", },
+ PidInfo { pid: "43", mode: "01", unit: "%", pid_name: "Absolute load value", formula: "(100/255) * (256A + B)", },
+ PidInfo { pid: "44", mode: "01", unit: "ratio", pid_name: "Commanded Air-Fuel Equivalence Ratio", formula: "(2/65536) * (256A + B)", },
+ PidInfo { pid: "45", mode: "01", unit: "%", pid_name: "Relative throttle position", formula: "100/255 * A", },
+ PidInfo { pid: "46", mode: "01", unit: "°C", pid_name: "Ambient air temperature", formula: "A - 40", },
+ PidInfo { pid: "47", mode: "01", unit: "%", pid_name: "Absolute throttle position B", formula: "100/255 * A", },
+ PidInfo { pid: "4D", mode: "01", unit: "min", pid_name: "Time run with MIL on", formula: "256A + B", },
+ PidInfo { pid: "4F", mode: "01", unit: "ratio, V, mA, kPa", pid_name: "Maximum value for AFR, oxygen sensor voltage, oxygen sensor current, and intake manifold absolute pressure", formula: "A, B, C, D * 10", },
+ PidInfo { pid: "50", mode: "01", unit: "g/s", pid_name: "Maximum air flow rate from MAF sensor", formula: "A * 10", },
+ PidInfo { pid: "51", mode: "01", unit: "", pid_name: "Fuel Type", formula: "", },
+ PidInfo { pid: "52", mode: "01", unit: "%", pid_name: "Ethanol fuel %", formula: "100/255 * A", },
+ PidInfo { pid: "53", mode: "01", unit: "kPa", pid_name: "Absolute Evap system Vapor Pressure", formula: "((256 * A)+B) / 200", },
+ PidInfo { pid: "54", mode: "01", unit: "Pa", pid_name: "Evap system vapor pressure", formula: "(256 * A) + B", },
+ PidInfo { pid: "55", mode: "01", unit: "%", pid_name: "Short term secondary oxygen sensor trim, A: bank 1, B: bank 3", formula: "", },
+ PidInfo { pid: "59", mode: "01", unit: "kPa", pid_name: "Fuel rail absolute pressure", formula: "10(256A + B)", },
+ PidInfo { pid: "5A", mode: "01", unit: "%", pid_name: "Relative accelerator pedal position", formula: "100/255 * A", },
+ PidInfo { pid: "5B", mode: "01", unit: "%", pid_name: "Hybrid battery pack remaining life", formula: "100/255 * A", },
+ PidInfo { pid: "5C", mode: "01", unit: "°C", pid_name: "Engine oil temperature", formula: "A - 40", },
+ PidInfo { pid: "5D", mode: "01", unit: "°", pid_name: "Fuel injection timing", formula: "(((256 * A)+B) / 128) - 210", },
+ PidInfo { pid: "5E", mode: "01", unit: "L/h", pid_name: "Engine fuel rate", formula: "((256 * A)+B) / 20", },
+ PidInfo { pid: "5F", mode: "01", unit: "", pid_name: "Emission requirements to which vehicle is designed", formula: "", },
+ PidInfo { pid: "61", mode: "01", unit: "%", pid_name: "Driver's demand engine - percent torque", formula: "A - 125", },
+ PidInfo { pid: "62", mode: "01", unit: "%", pid_name: "Actual engine - percent torque", formula: "A - 125", },
+ PidInfo { pid: "63", mode: "01", unit: "Nm", pid_name: "Engine reference torque", formula: "256A + B", },
+ PidInfo { pid: "64", mode: "01", unit: "%", pid_name: "Engine percent torque data", formula: "Subtract 125 from A - E", },
+ PidInfo { pid: "65", mode: "01", unit: "", pid_name: "Auxiliary input / output supported", formula: "", },
+ PidInfo { pid: "66", mode: "01", unit: "g/s", pid_name: "Mass air flow sensor", formula: "{A0}== Sensor A Supported", },
+ PidInfo { pid: "67", mode: "01", unit: "°C", pid_name: "Engine coolant temperature", formula: "{A0}== Sensor 1 Supported", },
+ PidInfo { pid: "68", mode: "01", unit: "°C", pid_name: "Intake air temperature sensor", formula: "{A0}== Sensor 1 Supported", },
+ PidInfo { pid: "69", mode: "01", unit: "", pid_name: "Actual EGR, Commanded EGR, and EGR Error", formula: "", },
+ PidInfo { pid: "6A", mode: "01", unit: "", pid_name: "Commanded Diesel intake air flow control and relative intake air flow position", formula: "", },
+ PidInfo { pid: "6B", mode: "01", unit: "", pid_name: "Exhaust gas recirculation temperature", formula: "", },
+ PidInfo { pid: "6C", mode: "01", unit: "", pid_name: "Commanded throttle actuator control and relative throttle position", formula: "", },
+ PidInfo { pid: "6D", mode: "01", unit: "", pid_name: "Fuel pressure control system", formula: "", },
+ PidInfo { pid: "6E", mode: "01", unit: "", pid_name: "Injection pressure control system", formula: "", },
+ PidInfo { pid: "6F", mode: "01", unit: "", pid_name: "Turbocharger compressor inlet pressure", formula: "", },
+ PidInfo { pid: "70", mode: "01", unit: "", pid_name: "Boost pressure control", formula: "", },
+ PidInfo { pid: "71", mode: "01", unit: "", pid_name: "Variable Geometry turbo (VGT) control", formula: "", },
+ PidInfo { pid: "72", mode: "01", unit: "", pid_name: "Wastegate control", formula: "", },
+ PidInfo { pid: "73", mode: "01", unit: "", pid_name: "Exhaust pressure", formula: "", },
+ PidInfo { pid: "74", mode: "01", unit: "RPM", pid_name: "Turbocharger RPM", formula: "", },
+ PidInfo { pid: "75", mode: "01", unit: "°C", pid_name: "Turbocharger temperature", formula: "", },
+ PidInfo { pid: "76", mode: "01", unit: "°C", pid_name: "Turbocharger temperature", formula: "", },
+ PidInfo { pid: "77", mode: "01", unit: "°C", pid_name: "Charge air cooler temperature (CACT)", formula: "", },
+ PidInfo { pid: "78", mode: "01", unit: "°C", pid_name: "Exhaust Gas temperature (EGT) Bank 1", formula: "", },
+ PidInfo { pid: "79", mode: "01", unit: "°C", pid_name: "Exhaust Gas temperature (EGT) Bank 2", formula: "", },
+ PidInfo { pid: "7A", mode: "01", unit: "", pid_name: "Diesel particulate filter (DPF)", formula: "", },
+ PidInfo { pid: "7B", mode: "01", unit: "", pid_name: "Diesel particulate filter (DPF)", formula: "", },
+ PidInfo { pid: "7C", mode: "01", unit: "°C", pid_name: "Diesel Particulate filter (DPF) temperature", formula: "(((256 * A)+B) / 10) - 40", },
+ PidInfo { pid: "7D", mode: "01", unit: "", pid_name: "NOx NTE", formula: "", },
+ PidInfo { pid: "7E", mode: "01", unit: "", pid_name: "PM NTE", formula: "", },
+ PidInfo { pid: "7F", mode: "01", unit: "s", pid_name: "Engine run time", formula: "B(2^24) + C(2^16) + D(2^8) + E", },
+ PidInfo { pid: "81", mode: "01", unit: "", pid_name: "Engine run time for Auxiliary Emissions Control Device(AECD)", formula: "", },
+ PidInfo { pid: "82", mode: "01", unit: "", pid_name: "Engine run time for Auxiliary Emissions Control Device(AECD)", formula: "", },
+ PidInfo { pid: "83", mode: "01", unit: "", pid_name: "NOx sensor", formula: "", },
+ PidInfo { pid: "84", mode: "01", unit: "", pid_name: "Manifold surface temperature", formula: "", },
+ PidInfo { pid: "85", mode: "01", unit: "%", pid_name: "NOx reagent system", formula: "100/255 * F", },
+ PidInfo { pid: "86", mode: "01", unit: "", pid_name: "Particulate matter (PM) sensor", formula: "", },
+ PidInfo { pid: "87", mode: "01", unit: "kPa", pid_name: "Intake manifold absolute pressure", formula: "", },
+ PidInfo { pid: "88", mode: "01", unit: "", pid_name: "SCR Induce System", formula: "", },
+ PidInfo { pid: "89", mode: "01", unit: "", pid_name: "Run Time for AECD #11-#15", formula: "", },
+ PidInfo { pid: "8A", mode: "01", unit: "", pid_name: "Run Time for AECD #16-#20", formula: "", },
+ PidInfo { pid: "8B", mode: "01", unit: "", pid_name: "Diesel Aftertreatment", formula: "", },
+ PidInfo { pid: "8C", mode: "01", unit: "", pid_name: "O2 Sensor (Wide Range)", formula: "", },
+ PidInfo { pid: "8D", mode: "01", unit: "%", pid_name: "Throttle Position G", formula: "", },
+ PidInfo { pid: "8E", mode: "01", unit: "%", pid_name: "Engine Friction - Percent Torque", formula: "A - 125", },
+ PidInfo { pid: "8F", mode: "01", unit: "", pid_name: "PM Sensor Bank 1 & 2", formula: "", },
+ PidInfo { pid: "90", mode: "01", unit: "h", pid_name: "WWH-OBD Vehicle OBD System Information", formula: "", },
+ PidInfo { pid: "91", mode: "01", unit: "h", pid_name: "WWH-OBD Vehicle OBD System Information", formula: "", },
+ PidInfo { pid: "92", mode: "01", unit: "", pid_name: "Fuel System Control", formula: "", },
+ PidInfo { pid: "93", mode: "01", unit: "h", pid_name: "WWH-OBD Vehicle OBD Counters support", formula: "", },
+ PidInfo { pid: "94", mode: "01", unit: "", pid_name: "NOx Warning And Inducement System", formula: "", },
+ PidInfo { pid: "98", mode: "01", unit: "°C", pid_name: "Exhaust Gas Temperature Sensor", formula: "", },
+ PidInfo { pid: "99", mode: "01", unit: "°C", pid_name: "Exhaust Gas Temperature Sensor", formula: "", },
+ PidInfo { pid: "9A", mode: "01", unit: "", pid_name: "Hybrid/EV Vehicle System Data, Battery, Voltage", formula: "", },
+ PidInfo { pid: "9B", mode: "01", unit: "%", pid_name: "Diesel Exhaust Fluid Sensor Data", formula: "100/255 * D", },
+ PidInfo { pid: "9C", mode: "01", unit: "", pid_name: "O2 Sensor Data", formula: "", },
+ PidInfo { pid: "9D", mode: "01", unit: "g/s", pid_name: "Engine Fuel Rate", formula: "", },
+ PidInfo { pid: "9E", mode: "01", unit: "kg/h", pid_name: "Engine Exhaust Flow Rate", formula: "", },
+ PidInfo { pid: "9F", mode: "01", unit: "", pid_name: "Fuel System Percentage Use", formula: "", },
+ PidInfo { pid: "A1", mode: "01", unit: "ppm", pid_name: "NOx Sensor Corrected Data", formula: "", },
+ PidInfo { pid: "A2", mode: "01", unit: "mg/stroke", pid_name: "Cylinder Fuel Rate", formula: "((256 * A)+B) / 32", },
+ PidInfo { pid: "A3", mode: "01", unit: "Pa", pid_name: "Evap System Vapor Pressure", formula: "", },
+ PidInfo { pid: "A4", mode: "01", unit: "ratio", pid_name: "Transmission Actual Gear", formula: "((256 * C) + D) / 1000", },
+ PidInfo { pid: "A5", mode: "01", unit: "%", pid_name: "Commanded Diesel Exhaust Fluid Dosing", formula: "B / 2", },
+ PidInfo { pid: "A6", mode: "01", unit: "", pid_name: "Odometer", formula: "(A(2^24) + B(2^16) + C(2^8) + D) / 10", },
+ PidInfo { pid: "A7", mode: "01", unit: "", pid_name: "NOx Sensor Concentration Sensors 3 and 4", formula: "", },
+ PidInfo { pid: "A8", mode: "01", unit: "", pid_name: "NOx Sensor Corrected Concentration Sensors 3 and 4", formula: "", },
+ PidInfo { pid: "A9", mode: "01", unit: "", pid_name: "ABS Disable Switch State", formula: "{A0}= 1:Supported; 0:Unsupported", },
+ PidInfo { pid: "C3", mode: "01", unit: "%", pid_name: "Fuel Level Input A/B", formula: "", },
+ PidInfo { pid: "C4", mode: "01", unit: "seconds / Count", pid_name: "Exhaust Particulate Control System Diagnostic Time/Count", formula: "", },
+ PidInfo { pid: "C5", mode: "01", unit: "kPa", pid_name: "Fuel Pressure A and B", formula: "", },
+ PidInfo { pid: "C7", mode: "01", unit: "km", pid_name: "Distance Since Reflash or Module Replacement", formula: "", },
+];
diff --git a/src/lib.rs b/backend/src/lib.rs
similarity index 74%
rename from src/lib.rs
rename to backend/src/lib.rs
index 83feaf7..140c30b 100644
--- a/src/lib.rs
+++ b/backend/src/lib.rs
@@ -1,16 +1,17 @@
-pub mod obd;
-pub mod vin;
mod cmd;
+pub mod dicts;
+pub mod obd;
mod pid;
mod replay;
mod response;
-mod scalar;
+pub mod scalar;
+pub mod vin;
-pub use pid::*;
-pub use response::*;
pub use cmd::*;
pub use obd::*;
+pub use pid::*;
+pub use response::*;
const CODE_DESC_DB_PATH: &str = "./data/code-descriptions.sqlite";
const MODE22_PIDS_DB_PATH: &str = "./data/model-pids.sqlite";
-const RECORDED_REQEUSTS_DIR: &str = "./data/requests.json";
\ No newline at end of file
+const RECORDED_REQEUSTS_DIR: &str = "./data/requests.json";
diff --git a/backend/src/main.rs b/backend/src/main.rs
new file mode 100644
index 0000000..83c6bca
--- /dev/null
+++ b/backend/src/main.rs
@@ -0,0 +1,97 @@
+// Prevents additional console window on Windows in release, DO NOT REMOVE!!
+#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+
+mod bridge;
+mod stats;
+
+use bridge::events::{
+ do_send_connection_status, listen_connect_elm, listen_decode_vin, listen_send_pids,
+ listen_send_ports,
+};
+use obdium::OBD;
+use stats::{
+ critical_frequency_calls, frequent_calls, high_frequency_calls, less_frequent_calls,
+ once_calls, oxygen_sensors,
+};
+
+use std::{
+ sync::{
+ atomic::{AtomicBool, Ordering},
+ Arc, Mutex,
+ },
+ thread::sleep,
+ time::Duration,
+};
+use tauri::{async_runtime::spawn, Manager, Window};
+
+fn track_data(window: &Arc, obd: &Arc>) {
+ critical_frequency_calls(window, obd);
+ high_frequency_calls(window, obd);
+ frequent_calls(window, obd);
+ less_frequent_calls(window, obd);
+ oxygen_sensors(window, obd);
+ once_calls(window, obd);
+}
+
+fn connect_obd(window: &Window, port: String, baud_rate: u32, protocol: u8) -> Option {
+ // Try to connect obd
+ let mut obd = OBD::new();
+
+ match obd.connect(&port, baud_rate, protocol) {
+ Ok(()) => {
+ let band = obd.serial_port_baud_rate().unwrap_or_default();
+ let port = obd.serial_port_name().unwrap_or_default();
+
+ do_send_connection_status(
+ window,
+ &obd,
+ format!("Connected to port {port} on {band} band"),
+ true,
+ );
+
+ Some(obd)
+ }
+ Err(error) => {
+ do_send_connection_status(
+ window,
+ &obd,
+ format!("Failed to connect. Error: {error}"),
+ false,
+ );
+
+ None
+ }
+ }
+}
+
+fn main() {
+ tauri::Builder::default()
+ .setup(|app| {
+ let window = app.get_window("main").unwrap();
+ let frontend_ready = Arc::new(AtomicBool::new(false));
+
+ spawn(async move {
+ listen_decode_vin(&window);
+
+ // Detect when the frontend is loaded
+ let frontend_ready_listener = Arc::clone(&frontend_ready);
+ window.listen("frontend-loaded", move |_| {
+ frontend_ready_listener.store(true, Ordering::SeqCst);
+ });
+
+ let window_arc = Arc::new(window);
+ listen_send_pids(&window_arc);
+
+ while !frontend_ready.load(Ordering::SeqCst) {
+ sleep(Duration::from_millis(100));
+ }
+
+ listen_send_ports(&window_arc);
+ listen_connect_elm(&window_arc);
+ });
+
+ Ok(())
+ })
+ .run(tauri::generate_context!())
+ .expect("error while running tauri application");
+}
diff --git a/src/obd.rs b/backend/src/obd.rs
similarity index 85%
rename from src/obd.rs
rename to backend/src/obd.rs
index 2dd0d81..0e0b37e 100644
--- a/src/obd.rs
+++ b/backend/src/obd.rs
@@ -92,7 +92,7 @@ impl OBD {
Self::default()
}
- pub fn connect(&mut self, port: &str, baud_rate: u32) -> Result<(), Error> {
+ pub fn connect(&mut self, port: &str, baud_rate: u32, protocol: u8) -> Result<(), Error> {
if self.replay_requests {
// No connection required
return Ok(());
@@ -103,11 +103,104 @@ impl OBD {
}
self.connection = serialport::new(port, baud_rate)
- .timeout(Duration::from_secs(10))
+ .timeout(Duration::from_secs(3))
.open()
.ok();
- self.init()
+ if self.connected() {
+ let initialized = self.init();
+ let mut command = match protocol {
+ 0 => Command::new_at(b"ATSP0"),
+ 1 => Command::new_at(b"ATSP1"),
+ 2 => Command::new_at(b"ATSP2"),
+ 3 => Command::new_at(b"ATSP3"),
+ 4 => Command::new_at(b"ATSP4"),
+ 5 => Command::new_at(b"ATSP5"),
+ 6 => Command::new_at(b"ATSP6"),
+ 7 => Command::new_at(b"ATSP7"),
+ 8 => Command::new_at(b"ATSP8"),
+ 9 => Command::new_at(b"ATSP9"),
+ _ => return Err(Error::InitFailed),
+ };
+
+ self.send_command(&mut command)?;
+
+ initialized
+ } else {
+ Err(Error::ConnectionFailed)
+ }
+ }
+
+ pub fn disconnect(&mut self) {
+ if let Some(connection) = self.connection.take() {
+ drop(connection);
+ self.connection = None;
+ }
+ }
+
+ pub fn connected(&self) -> bool {
+ self.connection.is_some()
+ }
+
+ pub fn serial_port_name(&self) -> Option {
+ match &self.connection {
+ Some(connection) => connection.name(),
+ None => None,
+ }
+ }
+
+ pub fn serial_port_baud_rate(&self) -> Option {
+ match &self.connection {
+ Some(connection) => connection.baud_rate().ok(),
+ None => None,
+ }
+ }
+
+ pub fn get_open_serial_port() -> String {
+ match serialport::available_ports() {
+ Ok(ports) => {
+ for port in ports.iter() {
+ // test each port with a simple AT command
+
+ if let Ok(mut com) = serialport::new(&port.port_name, 38400)
+ .timeout(Duration::from_millis(300))
+ .open()
+ {
+ let _ = com.clear(serialport::ClearBuffer::All);
+
+ if com.write_all(b"ATI\r").is_err() {
+ continue;
+ }
+
+ let mut buffer = [0u8; 1];
+ let mut response = String::new();
+
+ loop {
+ match com.read(&mut buffer) {
+ Ok(1) => {
+ let byte = buffer[0];
+ response.push(byte as char);
+ }
+ Ok(0) => std::thread::sleep(std::time::Duration::from_millis(10)),
+ Ok(_) => break,
+ Err(_) => break,
+ }
+ }
+
+ let as_string = String::from_utf8_lossy(&buffer);
+ let cleaned = as_string.trim_matches(|c: char| !c.is_ascii_graphic());
+
+ if cleaned.ends_with('>') {
+ return port.port_name.clone();
+ }
+ }
+ }
+ }
+ Err(err) => {
+ println!("error getting available serial ports: {err}");
+ }
+ }
+ String::new()
}
pub fn init(&mut self) -> Result<(), Error> {
@@ -117,11 +210,10 @@ impl OBD {
// and the ECU wont understand what we're asking for.
// Furthermore, causes unexpected behaviour when parsing the response.
let commands = vec![
- Command::new_at(b"ATZ"), // Reset all
- Command::new_at(b"ATE0"), // Echo off
- Command::new_at(b"ATL0"), // Linefeeds off
- Command::new_at(b"ATH1"), // Headers on
- Command::new_at(b"ATSP0"), // Automatic protocol
+ Command::new_at(b"ATZ"), // Reset all
+ Command::new_at(b"ATE0"), // Echo off
+ Command::new_at(b"ATL0"), // Linefeeds off
+ Command::new_at(b"ATH1"), // Headers on
];
for mut command in commands {
@@ -221,9 +313,12 @@ impl OBD {
}
cmd.push(b'\r');
- stream
- .write_all(&cmd)
- .map_err(|_| Error::ELM327WriteError)?;
+ match stream.write_all(&cmd) {
+ Ok(()) => (),
+
+ // Connection dropped
+ Err(_) => self.connection = None,
+ }
Ok(())
}
@@ -253,10 +348,11 @@ impl OBD {
let ecu_names = self.extract_ecu_names(&response);
let escaped = response.clone();
- response = response
- .replace(" ", "")
- .replace("\n", "")
- .replace(format!("{:02X}", payload_size).as_str(), "");
+ response = response.replace(" ", "").replace("\n", "").replacen(
+ format!("{:02X}", payload_size).as_str(),
+ "",
+ 1,
+ );
self.strip_ecu_names(&mut response, ecu_names.as_slice());
@@ -543,12 +639,10 @@ impl OBD {
);
}
- println!("vin: {vin}");
-
- match VIN::new(vin) {
+ match VIN::new(&vin) {
Ok(vin) => Some(vin),
Err(err) => {
- println!("error when getting vin. {err}");
+ println!("error when getting vin {vin}. {err}");
None
}
}
@@ -573,7 +667,7 @@ impl OBD {
}
match self.send_command(&mut request) {
- Ok(_) => {}
+ Ok(_) => (),
Err(err) => {
println!(
"{}\tAT: '{}' - PID: '{}' ",
diff --git a/src/pid/air.rs b/backend/src/pid/air.rs
similarity index 86%
rename from src/pid/air.rs
rename to backend/src/pid/air.rs
index c87816c..2ee29d1 100644
--- a/src/pid/air.rs
+++ b/backend/src/pid/air.rs
@@ -1,8 +1,6 @@
use crate::{
- Command,
- SensorNumber,
- OBD,
- scalar::{Scalar, Unit}
+ scalar::{Scalar, Unit},
+ Command, SensorNumber, OBD,
};
#[derive(Debug)]
@@ -33,6 +31,11 @@ impl OBD {
.map_no_data(|r| Scalar::new(r.a_value() - 40.0, Unit::Celsius))
}
+ pub fn intake_manifold_abs_pressure(&mut self) -> Scalar {
+ self.query(Command::new_pid(b"010B"))
+ .map_no_data(|r| Scalar::new(r.a_value(), Unit::KiloPascal))
+ }
+
pub fn maf_air_flow_rate(&mut self) -> Scalar {
self.query(Command::new_pid(b"0110")).map_no_data(|r| {
Scalar::new(
@@ -52,6 +55,9 @@ impl OBD {
.map_no_data(|r| Scalar::new(r.a_value() * 10.0, Unit::GramsPerSecond))
}
+ // Returns 2 values
+ // Voltage being given to the sensor
+ // Short term fuel trim
pub fn read_oxygen_sensor(&mut self, sensor: &SensorNumber) -> (Scalar, Scalar) {
let command = match sensor {
SensorNumber::Sensor1 => Command::new_pid(b"0114"),
@@ -78,7 +84,16 @@ impl OBD {
)
}
- pub fn o2_sensor_air_fuel_ratio(&mut self, sensor: &SensorNumber) -> (Scalar, Scalar) {
+ // Returns 2 values
+ // ABCD stands for the bytes in the payload and
+ // what they represent.
+ //
+ // AB (AB Bytes) - Air-Fuel equivalance ratio
+ // CD (CD Bytes) - Voltage
+ //
+ // Unlike read_oxygen_sensor, this doesn't return the
+ // short term fuel trim.
+ pub fn read_oxygen_sensor_abcd(&mut self, sensor: &SensorNumber) -> (Scalar, Scalar) {
let command = match sensor {
SensorNumber::Sensor1 => Command::new_pid(b"0124"),
SensorNumber::Sensor2 => Command::new_pid(b"0125"),
diff --git a/src/pid/diagnostics.rs b/backend/src/pid/diagnostics.rs
similarity index 99%
rename from src/pid/diagnostics.rs
rename to backend/src/pid/diagnostics.rs
index 5054b8b..d0c4be2 100644
--- a/src/pid/diagnostics.rs
+++ b/backend/src/pid/diagnostics.rs
@@ -2,12 +2,9 @@ use sqlite::State;
use std::fmt;
use crate::{
- Command,
- OBD,
- Error,
- CODE_DESC_DB_PATH,
engine::EngineType,
scalar::{Scalar, Unit},
+ Command, Error, CODE_DESC_DB_PATH, OBD,
};
#[derive(Debug)]
diff --git a/src/pid/engine.rs b/backend/src/pid/engine.rs
similarity index 95%
rename from src/pid/engine.rs
rename to backend/src/pid/engine.rs
index 35128b6..5529195 100644
--- a/src/pid/engine.rs
+++ b/backend/src/pid/engine.rs
@@ -1,9 +1,8 @@
use std::fmt;
use crate::{
- Command,
- SensorNumber, Service, OBD,
scalar::{Scalar, Unit},
+ Command, SensorNumber, Service, OBD,
};
#[derive(PartialEq, Eq)]
@@ -80,7 +79,7 @@ impl OBD {
.map_no_data(|r| Scalar::new((256.0 * r.a_value()) + r.b_value(), Unit::Seconds))
}
- pub fn engine_runtime_diesel(&mut self) -> Scalar {
+ fn engine_runtime_diesel(&mut self) -> Scalar {
let response = self.query(Command::new_pid(b"017F"));
if *response.get_payload_size() == 0 {
return Scalar::no_data();
@@ -171,6 +170,12 @@ impl OBD {
.map_no_data(|r| Scalar::new((256.0 * r.a_value()) + r.b_value(), Unit::NewtonMeters))
}
+ // Returns 5 values.
+ // idle - torque at idle
+ // engine_point_1 - torque at engine point 1
+ // engine_point_2 - torque at engine point 2
+ // engine_point_3 - torque at engine point 3
+ // engine_point_4 - torque at engine point 4
pub fn engine_percent_torque_data(&mut self) -> (Scalar, Scalar, Scalar, Scalar, Scalar) {
let response = self.query(Command::new_pid(b"0164"));
if *response.get_payload_size() == 0 {
diff --git a/src/pid/exhaust.rs b/backend/src/pid/exhaust.rs
similarity index 66%
rename from src/pid/exhaust.rs
rename to backend/src/pid/exhaust.rs
index 379454b..8952da7 100644
--- a/src/pid/exhaust.rs
+++ b/backend/src/pid/exhaust.rs
@@ -1,7 +1,6 @@
use crate::{
- Command,
- BankNumber, SensorNumber, OBD,
scalar::{Scalar, Unit},
+ BankNumber, Command, SensorNumber, OBD,
};
impl OBD {
@@ -37,23 +36,22 @@ impl OBD {
})
}
- pub fn boost_pressure_control(&self) -> f32 {
- todo!()
- }
-
- pub fn turbocharger_rpm(&self) -> f32 {
- todo!()
- }
+ pub fn boost_guage_pressure(&mut self) -> Scalar {
+ let map = self.intake_manifold_abs_pressure();
+ let baro = self.abs_barometric_pressure();
- pub fn turbocharger_temp(&self) -> f32 {
- todo!()
+ // result in kPa, gets converted to PSI. if conversion fails, do no data
+ // conversion never fail. map and baro will always be Unit::Kilopascal
+ (map - baro).to(Unit::PSI).unwrap_or(Scalar::no_data())
}
- pub fn exhaust_pressure(&self) -> f32 {
- todo!()
+ pub fn turbocharger_rpm(&mut self) -> Scalar {
+ self.query(Command::new_pid(b"0174"))
+ .map_no_data(|r| Scalar::new((256.0 * r.a_value()) + r.b_value(), Unit::RPM))
}
- pub fn exhaust_gas_temp(&self) -> f32 {
- todo!()
+ // TODO!
+ pub fn exhaust_gas_temp(&self) -> Scalar {
+ Scalar::no_data()
}
}
diff --git a/src/pid/fuel.rs b/backend/src/pid/fuel.rs
similarity index 96%
rename from src/pid/fuel.rs
rename to backend/src/pid/fuel.rs
index e9443a1..48462ce 100644
--- a/src/pid/fuel.rs
+++ b/backend/src/pid/fuel.rs
@@ -1,9 +1,8 @@
use std::fmt;
use crate::{
- Command,
- BankNumber, OBD,
- scalar::{Scalar, Unit}
+ scalar::{Scalar, Unit},
+ BankNumber, Command, OBD,
};
#[derive(Debug)]
@@ -71,7 +70,7 @@ impl FuelSystemStatus {
}
impl OBD {
- pub fn short_term_fuel_trim(&mut self, bank: BankNumber) -> Scalar {
+ pub fn short_term_fuel_trim(&mut self, bank: &BankNumber) -> Scalar {
let mut command = Command::default();
match bank {
@@ -83,7 +82,7 @@ impl OBD {
.map_no_data(|r| Scalar::new((r.a_value() / 1.28) - 100.0, Unit::Percent))
}
- pub fn long_term_fuel_trim(&mut self, bank: BankNumber) -> Scalar {
+ pub fn long_term_fuel_trim(&mut self, bank: &BankNumber) -> Scalar {
let command = match bank {
BankNumber::Bank1 => Command::new_pid(b"0107"),
BankNumber::Bank2 => Command::new_pid(b"0109"),
diff --git a/src/pid/mod.rs b/backend/src/pid/mod.rs
similarity index 82%
rename from src/pid/mod.rs
rename to backend/src/pid/mod.rs
index 60ddb3d..b8719fe 100644
--- a/src/pid/mod.rs
+++ b/backend/src/pid/mod.rs
@@ -3,4 +3,4 @@ pub mod diagnostics;
pub mod engine;
pub mod exhaust;
pub mod fuel;
-pub mod sensors;
\ No newline at end of file
+pub mod sensors;
diff --git a/src/pid/sensors.rs b/backend/src/pid/sensors.rs
similarity index 98%
rename from src/pid/sensors.rs
rename to backend/src/pid/sensors.rs
index 5f447ff..36a3601 100644
--- a/src/pid/sensors.rs
+++ b/backend/src/pid/sensors.rs
@@ -1,7 +1,6 @@
use crate::{
- Command,
- SensorNumber, OBD,
- scalar::{Scalar, Unit}
+ scalar::{Scalar, Unit},
+ Command, SensorNumber, OBD,
};
impl OBD {
diff --git a/src/replay.rs b/backend/src/replay.rs
similarity index 85%
rename from src/replay.rs
rename to backend/src/replay.rs
index 659b084..7b89087 100644
--- a/src/replay.rs
+++ b/backend/src/replay.rs
@@ -10,13 +10,26 @@ use serde_json::{json, Value};
impl OBD {
pub fn record_requests(&mut self, state: bool) {
- // clear requests file
- if fs::File::create(RECORDED_REQEUSTS_DIR).is_ok() && state {
- self.record_requests = state;
+ if state {
+ match fs::OpenOptions::new()
+ .write(true)
+ .create(true)
+ .append(true)
+ .open(RECORDED_REQEUSTS_DIR)
+ {
+ Ok(_) => {
+ self.record_requests = true;
+ }
+ Err(e) => {
+ println!("File creation to save requests failed: {e}. Not recording requests.");
+ self.record_requests = false;
+ }
+ }
} else {
- println!("file creation to save requests failed. not recording requests.");
self.record_requests = false;
}
+
+ println!("Recording requests: {state:?}");
}
pub fn replay_requests(&mut self, state: bool) {
@@ -26,7 +39,8 @@ impl OBD {
self.record_requests = false;
}
- self.replay_requests = state
+ self.replay_requests = state;
+ println!("Replaying requests: {state:?}");
}
pub(crate) fn save_request(&mut self, request: &Command, response: &Response) {
diff --git a/src/response.rs b/backend/src/response.rs
similarity index 100%
rename from src/response.rs
rename to backend/src/response.rs
diff --git a/src/scalar.rs b/backend/src/scalar.rs
similarity index 84%
rename from src/scalar.rs
rename to backend/src/scalar.rs
index 22f2eb9..2e8710f 100644
--- a/src/scalar.rs
+++ b/backend/src/scalar.rs
@@ -1,6 +1,11 @@
-use std::{fmt, str::FromStr};
-
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+use serde::{Deserialize, Serialize};
+use std::{
+ fmt,
+ ops::{Add, Sub},
+ str::FromStr,
+};
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub enum Unit {
Percent,
Ratio,
@@ -100,6 +105,7 @@ impl Unit {
Unit::PartsPerMillion => "ppm",
Unit::MiligramsPerStroke => "mg/stroke",
Unit::PSI => "PSI",
+ Unit::NoData => "NO DATA",
_ => "",
}
}
@@ -121,6 +127,24 @@ impl fmt::Display for Scalar {
}
}
+// Doesn't matter what unit you are using.
+impl Sub for Scalar {
+ type Output = Scalar;
+
+ fn sub(self, other: Self) -> Self::Output {
+ Scalar::new(self.value - other.value, self.unit)
+ }
+}
+
+// Doesn't matter what unit you are using.
+impl Add for Scalar {
+ type Output = Scalar;
+
+ fn add(self, other: Self) -> Self::Output {
+ Scalar::new(self.value + other.value, self.unit)
+ }
+}
+
impl Scalar {
pub fn new(value: f32, unit: Unit) -> Self {
Self { value, unit }
@@ -175,6 +199,12 @@ impl Scalar {
(NewtonMeters, FootPounds) => Some(Scalar::new(self.value * 0.73756, FootPounds)),
(FootPounds, NewtonMeters) => Some(Scalar::new(self.value / 0.73756, NewtonMeters)),
+ // Pressure
+ (KiloPascal, PSI) => Some(Scalar::new(self.value / 6.895, Unit::PSI)),
+ (PSI, KiloPascal) => Some(Scalar::new(self.value * 6.895, Unit::KiloPascal)),
+ (KiloPascal, Pascal) => Some(Scalar::new(self.value * 1000.0, Unit::Pascal)),
+ (Pascal, KiloPascal) => Some(Scalar::new(self.value / 1000.0, Unit::KiloPascal)),
+
(_, _) => None,
}
}
diff --git a/backend/src/stats.rs b/backend/src/stats.rs
new file mode 100644
index 0000000..00507d6
--- /dev/null
+++ b/backend/src/stats.rs
@@ -0,0 +1,543 @@
+use obdium::{scalar::Scalar, BankNumber};
+use obdium::{SensorNumber, Service, OBD};
+use serde::{Deserialize, Serialize};
+use std::{
+ sync::{Arc, Mutex},
+ time::Duration,
+};
+use tauri::{async_runtime::spawn, Window};
+use tokio::time;
+
+macro_rules! update {
+ ($win:expr, $($name:expr => $val:expr),* $(,)?) => {
+ $(
+ update_card($win, $name, $val);
+ )*
+ };
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+struct Card {
+ name: String,
+ unit: String,
+ value: f32,
+}
+
+fn update_card(window: &Window, name: T, scalar: Scalar)
+where
+ T: Into + std::fmt::Debug,
+{
+ let card = Card {
+ name: name.into(),
+ unit: scalar.unit.as_str().to_string().to_uppercase(),
+ value: scalar.value.round(),
+ };
+
+ window.emit("update-card", card).unwrap();
+}
+
+pub fn critical_frequency_calls(window: &Arc, obd: &Arc>) {
+ let window = Arc::clone(window);
+ let obd = Arc::clone(obd);
+ spawn(async move {
+ let mut interval = time::interval(Duration::from_millis(500));
+ loop {
+ interval.tick().await;
+
+ let mut obd = obd.lock().unwrap();
+ if !obd.connected() {
+ break;
+ }
+
+ let rpm = obd.rpm();
+ let turbocharger_rpm = obd.turbocharger_rpm();
+ let vehicle_speed = obd.vehicle_speed();
+
+ drop(obd);
+
+ update!(
+ &window,
+ "Vehicle Speed" => vehicle_speed,
+ "Engine Speed" => rpm,
+ "Turbocharger RPM" => turbocharger_rpm,
+ );
+ }
+ });
+}
+
+pub fn high_frequency_calls(window: &Arc, obd: &Arc>) {
+ let window = Arc::clone(window);
+ let obd = Arc::clone(obd);
+
+ spawn(async move {
+ let mut interval = time::interval(Duration::from_secs(1));
+ let mut cycle = 0;
+ loop {
+ interval.tick().await;
+ cycle = (cycle + 1) % 5;
+
+ match cycle {
+ 0 => {
+ // Cycle 1
+ let (
+ maf_air_flow_rate,
+ engine_fuel_rate,
+ engine_oil_pressure,
+ drivers_demand_engine_torque,
+ actual_engine_torque,
+ ) = {
+ let mut obd = obd.lock().unwrap();
+ if !obd.connected() {
+ break;
+ }
+
+ (
+ obd.maf_air_flow_rate(),
+ obd.engine_fuel_rate(),
+ obd.engine_oil_pressure(),
+ obd.drivers_demand_engine_torque(),
+ obd.actual_engine_torque(),
+ )
+ };
+
+ update!(
+ &window,
+ "MAF Airflow Rate" => maf_air_flow_rate,
+ "Engine Fuel Rate" => engine_fuel_rate,
+ "Engine Oil Pressure" => engine_oil_pressure,
+ "Drivers Demand Engine Torque" => drivers_demand_engine_torque,
+ "Actual Engine Torque" => actual_engine_torque,
+ );
+ }
+ 1 => {
+ // Cycle 2
+ let (
+ engine_load,
+ ref_engine_torque,
+ fuel_pressure,
+ fuel_rail_pressure,
+ fuel_rail_gauge_pressure,
+ ) = {
+ let mut obd = obd.lock().unwrap();
+ (
+ obd.engine_load(),
+ obd.reference_engine_torque(),
+ obd.fuel_pressure(),
+ obd.fuel_rail_pressure(),
+ obd.fuel_rail_guage_pressure(),
+ )
+ };
+
+ update!(
+ &window,
+ "Engine Load" => engine_load,
+ "Reference Engine Torque" => ref_engine_torque,
+ "Fuel Pressure" => fuel_pressure,
+ "Fuel Rail Pressure" => fuel_rail_pressure,
+ "Fuel Rail Gauge Pressure" => fuel_rail_gauge_pressure,
+ );
+ }
+ 2 => {
+ // Cycle 3
+ let (
+ cylinder_fuel_rate,
+ max_air_flow_rate_maf,
+ timing_advance,
+ boost_guage_pressure,
+ ) = {
+ let mut obd = obd.lock().unwrap();
+ (
+ obd.cylinder_fuel_rate(),
+ obd.max_air_flow_rate_from_maf(),
+ obd.timing_advance(),
+ obd.boost_guage_pressure(),
+ )
+ };
+
+ update!(
+ &window,
+ "Cylinder Fuel Rate" => cylinder_fuel_rate,
+ "MAF Maximum Airflow Rate" => max_air_flow_rate_maf,
+ "Timing Advance" => timing_advance,
+ "Boost Gauge Pressure" => boost_guage_pressure,
+ );
+ }
+ 3 => {
+ // Cycle 4
+ let (
+ throttle_pos,
+ rel_throttle_pos,
+ abs_throttle_b,
+ abs_throttle_c,
+ accel_pedal_d,
+ ) = {
+ let mut obd = obd.lock().unwrap();
+ (
+ obd.throttle_position(),
+ obd.relative_throttle_pos(),
+ obd.abs_throttle_position_b(),
+ obd.abs_throttle_position_c(),
+ obd.acc_pedal_position_d(),
+ )
+ };
+
+ update!(
+ &window,
+ "Throttle Pos." => throttle_pos,
+ "Relative Throttle Pos." => rel_throttle_pos,
+ "Abs. Throttle Pos. (D)" => abs_throttle_b,
+ "Abs. Throttle Pos. (C)" => abs_throttle_c,
+ "Accelerator Pedal Pos. (D)" => accel_pedal_d,
+ );
+ }
+ 4 => {
+ // Cycle 5
+ let (acc_pedal_pos_e, acc_pedal_pos_f, engine_torque_data) = {
+ let mut obd = obd.lock().unwrap();
+ (
+ obd.acc_pedal_position_e(),
+ obd.acc_pedal_position_f(),
+ obd.engine_percent_torque_data(),
+ )
+ };
+
+ update!(
+ &window,
+ "Idle Engine Torque" => engine_torque_data.0,
+ "Engine Point 1 Torque" => engine_torque_data.1,
+ "Engine Point 2 Torque" => engine_torque_data.2,
+ "Engine Point 3 Torque" => engine_torque_data.3,
+ "Engine Point 4 Torque" => engine_torque_data.4,
+ "Accelerator Pedal Pos. (E)" => acc_pedal_pos_e,
+ "Accelerator Pedal Pos. (F)" => acc_pedal_pos_f,
+ );
+ }
+ _ => unreachable!(),
+ }
+ }
+ });
+}
+
+pub fn frequent_calls(window: &Arc, obd: &Arc>) {
+ let window = Arc::clone(window);
+ let obd = Arc::clone(obd);
+ spawn(async move {
+ let mut interval = time::interval(Duration::from_secs(4));
+ let mut cycles = 0;
+ loop {
+ interval.tick().await;
+
+ cycles = (cycles + 1) % 2;
+ match cycles {
+ 0 => {
+ let stft_bank_1 = {
+ let mut obd = obd.lock().unwrap();
+ if !obd.connected() {
+ break;
+ }
+ obd.short_term_fuel_trim(&BankNumber::Bank1)
+ };
+
+ update!(&window, "Short Term Fuel Trim (Bank 1)" => stft_bank_1);
+ }
+ 1 => {
+ let stft_bank_2 = {
+ let mut obd = obd.lock().unwrap();
+ obd.short_term_fuel_trim(&BankNumber::Bank2)
+ };
+
+ update!(
+ &window,
+ "Short Term Fuel Trim (Bank 2)" => stft_bank_2,
+ );
+ }
+ _ => unreachable!(),
+ }
+ }
+ });
+}
+
+pub fn less_frequent_calls(window: &Arc, obd: &Arc>) {
+ let window = Arc::clone(window);
+ let obd = Arc::clone(obd);
+ spawn(async move {
+ let mut interval = time::interval(Duration::from_secs(3));
+ let mut cycles = 0;
+ loop {
+ interval.tick().await;
+ cycles = (cycles + 1) % 4;
+
+ match cycles {
+ 0 => {
+ let (
+ ltft_bank_1,
+ ltft_bank_2,
+ coolant_temp,
+ coolant_temp_sensors,
+ engine_oil_temp,
+ engine_oil_temp_ext,
+ engine_oil_temp_sensors,
+ ) = {
+ let mut obd = obd.lock().unwrap();
+ if !obd.connected() {
+ break;
+ }
+ (
+ obd.long_term_fuel_trim(&BankNumber::Bank1),
+ obd.long_term_fuel_trim(&BankNumber::Bank2),
+ obd.coolant_temp(),
+ obd.coolant_temp_sensors(),
+ obd.engine_oil_temp(Service::Mode01),
+ obd.engine_oil_temp(Service::Mode22),
+ obd.engine_oil_temp_sensors(),
+ )
+ };
+
+ update!(
+ &window,
+ "Long Term Fuel Trim (Bank 1)" => ltft_bank_1,
+ "Long Term Fuel Trim (Bank 2)" => ltft_bank_2,
+ "Coolant Temp." => coolant_temp,
+ "Engine Oil Temp. (Mode 22)" => engine_oil_temp_ext,
+ "Engine Oil Temp. (Mode 01)" => engine_oil_temp,
+ "Coolant Temp. (Sensors: A)" => coolant_temp_sensors.0,
+ "Coolant Temp. (Sensors: B)" => coolant_temp_sensors.1,
+ "Engine Oil Temp. (Sensors: A)" => engine_oil_temp_sensors.0,
+ "Engine Oil Temp. (Sensors: B)" => engine_oil_temp_sensors.1,
+ );
+ }
+ 1 => {
+ let (
+ commanded_egr,
+ egr_error,
+ catalyst_temp_b1_s1,
+ catalyst_temp_b1_s2,
+ catalyst_temp_b2_s1,
+ catalyst_temp_b2_s2,
+ ) = {
+ let mut obd = obd.lock().unwrap();
+ (
+ obd.commanded_egr(),
+ obd.egr_error(),
+ obd.catalyst_temp(BankNumber::Bank1, SensorNumber::Sensor1),
+ obd.catalyst_temp(BankNumber::Bank1, SensorNumber::Sensor2),
+ obd.catalyst_temp(BankNumber::Bank2, SensorNumber::Sensor1),
+ obd.catalyst_temp(BankNumber::Bank2, SensorNumber::Sensor2),
+ )
+ };
+
+ update!(
+ &window,
+ "Catalyst Temp. (Bank 1: Sensor 1)" => catalyst_temp_b1_s1,
+ "Catalyst Temp. (Bank 1: Sensor 2)" => catalyst_temp_b1_s2,
+ "Catalyst Temp. (Bank 2: Sensor 1)" => catalyst_temp_b2_s1,
+ "Catalyst Temp. (Bank 2: Sensor 2)" => catalyst_temp_b2_s2,
+ "Commanded EGR" => commanded_egr,
+ "EGR Error" => egr_error,
+ );
+ }
+ 2 => {
+ let (
+ barometric_pressure,
+ ambient_air_temp,
+ max_values_for,
+ fuel_injection_timing,
+ ) = {
+ let mut obd = obd.lock().unwrap();
+ (
+ obd.abs_barometric_pressure(),
+ obd.ambient_air_temp(),
+ obd.max_values_for(),
+ obd.fuel_injection_timing(),
+ )
+ };
+
+ update!(
+ &window,
+ "Absolute Barometric Pressure" => barometric_pressure,
+ "Ambient Air Temp." => ambient_air_temp,
+ "Fuel Injection Timing" => fuel_injection_timing,
+ "Maximum AFR Value" => max_values_for.0,
+ "Maximum O2 Sensor Voltage" => max_values_for.1,
+ "Maximum O2 Sensor Current" => max_values_for.2,
+ "Maximum Intake Abs. Pressure" => max_values_for.3,
+ );
+ }
+ 3 => {
+ let (
+ commanded_evap_purge,
+ evap_sys_vapor_pressure,
+ control_mod_voltage,
+ engine_runtime,
+ ) = {
+ let mut obd = obd.lock().unwrap();
+ (
+ obd.commanded_evap_purge(),
+ obd.evap_system_vapor_pressure(),
+ obd.control_module_voltage(),
+ obd.engine_runtime(),
+ )
+ };
+
+ update!(
+ &window,
+ "Commanded EVAP Purge" => commanded_evap_purge,
+ "EVAP System Vapor Pressure" =>evap_sys_vapor_pressure,
+ "Control Module Voltage" => control_mod_voltage,
+ "Engine Runtime (Session)" => engine_runtime,
+ );
+ }
+ _ => unreachable!(),
+ }
+ }
+ });
+}
+
+pub fn oxygen_sensors(window: &Arc, obd: &Arc>) {
+ let window = Arc::clone(window);
+ let obd = Arc::clone(obd);
+ spawn(async move {
+ let mut interval = time::interval(Duration::from_secs(6));
+ let mut cycles = 0;
+ loop {
+ interval.tick().await;
+ cycles = (cycles + 1) % 4;
+
+ match cycles {
+ 0 => {
+ let (o2_sensor_1, o2_sensor_2, o2_sensor_3, o2_sensor_4) = {
+ let mut obd = obd.lock().unwrap();
+ if !obd.connected() {
+ break;
+ }
+ (
+ obd.read_oxygen_sensor(&SensorNumber::Sensor1),
+ obd.read_oxygen_sensor(&SensorNumber::Sensor2),
+ obd.read_oxygen_sensor(&SensorNumber::Sensor3),
+ obd.read_oxygen_sensor(&SensorNumber::Sensor4),
+ )
+ };
+
+ update!(
+ &window,
+ "O2 Sensor (1) Voltage (1)" => o2_sensor_1.0,
+ "O2 Sensor (1) STFT" => o2_sensor_1.1,
+ "O2 Sensor (2) Voltage (1)" => o2_sensor_2.0,
+ "O2 Sensor (2) STFT" => o2_sensor_2.1,
+ "O2 Sensor (3) Voltage (1)" => o2_sensor_3.0,
+ "O2 Sensor (3) STFT" => o2_sensor_3.1,
+ "O2 Sensor (4) Voltage (1)" => o2_sensor_4.0,
+ "O2 Sensor (4) STFT" => o2_sensor_4.1,
+ );
+ }
+ 1 => {
+ let (
+ o2_sensor_af_ratio_1,
+ o2_sensor_af_ratio_2,
+ o2_sensor_af_ratio_3,
+ o2_sensor_af_ratio_4,
+ ) = {
+ let mut obd = obd.lock().unwrap();
+ (
+ obd.read_oxygen_sensor_abcd(&SensorNumber::Sensor1),
+ obd.read_oxygen_sensor_abcd(&SensorNumber::Sensor2),
+ obd.read_oxygen_sensor_abcd(&SensorNumber::Sensor3),
+ obd.read_oxygen_sensor_abcd(&SensorNumber::Sensor4),
+ )
+ };
+
+ update!(
+ &window,
+ "O2 Sensor (1) AFR" => o2_sensor_af_ratio_1.0,
+ "O2 Sensor (1) Voltage (2)" => o2_sensor_af_ratio_1.1,
+ "O2 Sensor (2) AFR" => o2_sensor_af_ratio_2.0,
+ "O2 Sensor (2) Voltage (2)" => o2_sensor_af_ratio_2.1,
+ "O2 Sensor (3) AFR" => o2_sensor_af_ratio_3.0,
+ "O2 Sensor (3) Voltage (2)" => o2_sensor_af_ratio_3.1,
+ "O2 Sensor (4) AFR" => o2_sensor_af_ratio_4.0,
+ "O2 Sensor (4) Voltage (2)" => o2_sensor_af_ratio_4.1,
+ );
+ }
+ 2 => {
+ let (o2_sensor_5, o2_sensor_6, o2_sensor_7, o2_sensor_8) = {
+ let mut obd = obd.lock().unwrap();
+ (
+ obd.read_oxygen_sensor(&SensorNumber::Sensor5),
+ obd.read_oxygen_sensor(&SensorNumber::Sensor6),
+ obd.read_oxygen_sensor(&SensorNumber::Sensor7),
+ obd.read_oxygen_sensor(&SensorNumber::Sensor8),
+ )
+ };
+
+ update!(
+ &window,
+ "O2 Sensor (5) Voltage (1)" => o2_sensor_5.0,
+ "O2 Sensor (5) STFT" => o2_sensor_5.1,
+ "O2 Sensor (6) Voltage (1)" => o2_sensor_6.0,
+ "O2 Sensor (6) STFT" => o2_sensor_6.1,
+ "O2 Sensor (7) Voltage (1)" => o2_sensor_7.0,
+ "O2 Sensor (7) STFT" => o2_sensor_7.1,
+ "O2 Sensor (8) Voltage (1)" => o2_sensor_8.0,
+ "O2 Sensor (8) STFT" => o2_sensor_8.1,
+ );
+ }
+ 3 => {
+ let (
+ o2_sensor_af_ratio_5,
+ o2_sensor_af_ratio_6,
+ o2_sensor_af_ratio_7,
+ o2_sensor_af_ratio_8,
+ ) = {
+ let mut obd = obd.lock().unwrap();
+ (
+ obd.read_oxygen_sensor_abcd(&SensorNumber::Sensor5),
+ obd.read_oxygen_sensor_abcd(&SensorNumber::Sensor6),
+ obd.read_oxygen_sensor_abcd(&SensorNumber::Sensor7),
+ obd.read_oxygen_sensor_abcd(&SensorNumber::Sensor8),
+ )
+ };
+
+ update!(
+ &window,
+ "O2 Sensor (5) AFR" => o2_sensor_af_ratio_5.0,
+ "O2 Sensor (5) Voltage (2)" => o2_sensor_af_ratio_5.1,
+ "O2 Sensor (6) AFR" => o2_sensor_af_ratio_6.0,
+ "O2 Sensor (6) Voltage (2)" => o2_sensor_af_ratio_6.1,
+ "O2 Sensor (7) AFR" => o2_sensor_af_ratio_7.0,
+ "O2 Sensor (7) Voltage (2)" => o2_sensor_af_ratio_7.1,
+ "O2 Sensor (8) AFR" => o2_sensor_af_ratio_8.0,
+ "O2 Sensor (8) Voltage (2)" => o2_sensor_af_ratio_8.1,
+ );
+ }
+ _ => unreachable!(),
+ }
+ }
+ });
+}
+
+pub fn once_calls(window: &Arc, obd: &Arc>) {
+ // Once calls
+ let window = Arc::clone(window);
+ let obd = Arc::clone(obd);
+ spawn(async move {
+ let mut obd = obd.lock().unwrap();
+ let warm_ups = obd.warm_ups_since_codes_cleared();
+ let dist_since = obd.distance_traveled_since_codes_cleared();
+ let dist_with = obd.distance_traveled_with_mil();
+ let time_since = obd.time_since_codes_cleared();
+ let time_with = obd.time_run_with_mil();
+ let odometer = obd.odometer();
+ let ethanol_fuel_percent = obd.ethanol_fuel_percentage();
+
+ update!(
+ &window,
+ "Warm-Ups Since Codes Cleared" => warm_ups,
+ "Dist. Since Codes Cleared" => dist_since,
+ "Dist. With Check Engine Light" => dist_with,
+ "Time Since Codes Cleared" => time_since,
+ "Time With Check Engine Light" => time_with,
+ "Odometer" => odometer,
+ "Ethanol Fuel Percentage" => ethanol_fuel_percent,
+ );
+ });
+}
diff --git a/src/vin/attributes.rs b/backend/src/vin/attributes.rs
similarity index 92%
rename from src/vin/attributes.rs
rename to backend/src/vin/attributes.rs
index 82b6c60..6632f20 100644
--- a/src/vin/attributes.rs
+++ b/backend/src/vin/attributes.rs
@@ -1,3 +1,4 @@
+use chrono::Datelike;
use sqlite::State;
use std::str::FromStr;
@@ -11,9 +12,8 @@ impl VIN {
where
T::Err: std::fmt::Debug,
{
- let schema_id = self.get_vin_schema_id()?;
let key = self.as_key();
- let data = self.query_pattern(schema_id, element_id, key)?;
+ let data = self.query_pattern(element_id, key)?;
data.4.parse::().map_err(|_| Error::ParseError)
}
@@ -42,7 +42,7 @@ impl VIN {
let vin = self.get_vin();
let pos10 = vin.chars().nth(9).unwrap_or('\0');
- let model_year = match pos10 {
+ let mut model_year = match pos10 {
'A'..='H' => 2010 + (pos10 as i32) - ('A' as i32),
'J'..='N' => 2010 + (pos10 as i32) - ('A' as i32) - 1,
'P' => 2023,
@@ -58,29 +58,34 @@ impl VIN {
}
};
- // let mut car_lt = false;
- // let vehicle_type_id = self.get_vehicle_type_id()?;
- // let truck_type_id = self.get_truck_type_id()?;
+ let mut car_lt = false;
+ let vehicle_type_id = self.get_vehicle_type_id()?;
+ let truck_type_id = self.get_truck_type_id()?;
- // if (2..=7).contains(&vehicle_type_id) || (vehicle_type_id == 3 && truck_type_id == 1) {
- // car_lt = true;
- // }
+ if (2..=7).contains(&vehicle_type_id) || (vehicle_type_id == 3 && truck_type_id == 1) {
+ car_lt = true;
+ }
- // let pos7 = vin.chars().nth(6).unwrap_or('\0');
+ let pos7 = vin.chars().nth(6).unwrap_or('\0');
- // if car_lt {
- // if let '0'..='9' = pos7 {
- // model_year -= 30;
- // }
- // }
+ if car_lt {
+ if let '0'..='9' = pos7 {
+ model_year -= 30;
+ }
+ }
- // if model_year > chrono::Utc::now().year() + 1 {
- // model_year -= 30;
- // }
+ if model_year > chrono::Utc::now().year() + 1 {
+ model_year -= 30;
+ }
Ok(model_year)
}
+ pub fn get_vehicle_manufacturer(&self) -> Result {
+ let manufactur_id = self.get_manufacturer_id()?;
+ self.lookup_name_from_id("Manufacturer", manufactur_id)
+ }
+
pub fn get_engine_model(&self) -> Result {
self.get_attribute_id_from_pattern::(ElementId::EngineModel)
}
diff --git a/src/vin/element_ids.rs b/backend/src/vin/element_ids.rs
similarity index 100%
rename from src/vin/element_ids.rs
rename to backend/src/vin/element_ids.rs
diff --git a/src/vin/mod.rs b/backend/src/vin/mod.rs
similarity index 100%
rename from src/vin/mod.rs
rename to backend/src/vin/mod.rs
diff --git a/src/vin/parser.rs b/backend/src/vin/parser.rs
similarity index 94%
rename from src/vin/parser.rs
rename to backend/src/vin/parser.rs
index dcceef3..6e59d0b 100644
--- a/src/vin/parser.rs
+++ b/backend/src/vin/parser.rs
@@ -81,7 +81,6 @@ impl VIN {
}
}
- _vin.get_model_year()?;
_vin.checksum()?;
_vin.get_wmi();
@@ -223,8 +222,16 @@ impl VIN {
pub(crate) fn connect_to_vpic_db(&mut self) -> Result<&Connection, Error> {
if self.vpic_db_con.is_none() {
- let conn = Connection::open(VPIC_DB_PATH).map_err(|_| Error::VPICConnectFailed);
- self.vpic_db_con = conn.ok();
+ match Connection::open(VPIC_DB_PATH) {
+ Ok(con) => {
+ self.vpic_db_con = Some(con);
+ return self.vpic_connection();
+ }
+ Err(err) => {
+ println!("error: {err}");
+ return Err(Error::VPICConnectFailed);
+ }
+ }
}
self.vpic_connection()
diff --git a/src/vin/pattern.rs b/backend/src/vin/pattern.rs
similarity index 77%
rename from src/vin/pattern.rs
rename to backend/src/vin/pattern.rs
index a88273f..797fba0 100644
--- a/src/vin/pattern.rs
+++ b/backend/src/vin/pattern.rs
@@ -89,6 +89,32 @@ impl VIN {
key_chars.next().is_none()
}
+ pub fn get_organization_id(&self) -> Result {
+ let con = self.vpic_connection()?;
+ let query = "SELECT * FROM Wmi_VinSchema WHERE WmiId = ? and VinSchemaId = ?";
+ let wmi_id = self.get_wmi_id()?;
+ let vin_schema_id = self.get_vin_schema_id()?;
+ let mut statement = con
+ .prepare(query)
+ .map_err(|_| Error::VPICQueryError(query))?;
+
+ statement
+ .bind((1, wmi_id))
+ .map_err(|_| Error::VPICQueryError(query))?;
+
+ statement
+ .bind((2, vin_schema_id))
+ .map_err(|_| Error::VPICQueryError(query))?;
+
+ if let Ok(State::Row) = statement.next() {
+ statement
+ .read::("OrgId")
+ .map_err(|_| Error::VPICQueryError(query))
+ } else {
+ Err(Error::NoResultsFound(query))
+ }
+ }
+
/// Returns a row from Pattern table
/// that matches conditions:
/// 1. Schema ID
@@ -96,40 +122,54 @@ impl VIN {
/// 3. Key matches pattern 'Keys'
pub(crate) fn query_pattern(
&self,
- vin_schema_id: i64,
element_id: ElementId,
key: &str,
) -> Result<(i64, i64, String, i64, String), Error> {
let con = self.vpic_connection()?;
- let query = "SELECT * FROM Pattern WHERE VinSchemaId = ? and ElementId = ?";
+ let possible_vin_schema_ids = self.get_similiar_vin_schema_ids()?;
+ let placeholders = possible_vin_schema_ids
+ .iter()
+ .map(|_| "?")
+ .collect::>()
+ .join(", ");
+ let query = format!(
+ "SELECT * FROM Pattern WHERE VinSchemaId IN ({}) AND ElementId = ?",
+ placeholders
+ );
let mut statement = con
.prepare(query)
- .map_err(|_| Error::VPICQueryError(query))?;
+ .map_err(|_| Error::VPICQueryError("query pattern"))?;
- statement
- .bind((1, vin_schema_id))
- .map_err(|_| Error::VPICQueryError(query))?;
+ for (i, id) in possible_vin_schema_ids.iter().enumerate() {
+ statement
+ .bind((i + 1, *id))
+ .map_err(|_| Error::VPICQueryError("query pattern"))?;
+ }
statement
- .bind((2, element_id.as_i64()))
- .map_err(|_| Error::VPICQueryError(query))?;
+ .bind((possible_vin_schema_ids.len() + 1, element_id.as_i64()))
+ .map_err(|_| Error::VPICQueryError("query pattern"))?;
while let Ok(State::Row) = statement.next() {
let pattern = statement
.read::("Keys")
- .map_err(|_| Error::VPICQueryError(query))?;
+ .map_err(|_| Error::VPICQueryError("query pattern"))?;
let pattern_sql_like = pattern.replace("*", "_") + "%"; // simulate MSSQL logic
if VIN::match_pattern(key, &pattern_sql_like) {
let pattern_id = statement
.read::("Id")
- .map_err(|_| Error::VPICQueryError(query))?;
+ .map_err(|_| Error::VPICQueryError("query pattern"))?;
let attribute_id = statement
.read::("AttributeId")
- .map_err(|_| Error::VPICQueryError(query))?;
+ .map_err(|_| Error::VPICQueryError("query pattern"))?;
+
+ let vin_schema_id = statement
+ .read::("VinSchemaId")
+ .map_err(|_| Error::VPICQueryError("query pattern"))?;
return Ok((
pattern_id,
@@ -141,7 +181,23 @@ impl VIN {
}
}
- Err(Error::NoResultsFound(query))
+ // // Do not try any other possible vin_schema_ids
+ // if !retry {
+ // return Err(Error::NoResultsFound(query))
+ // }
+
+ // // WORST CASE - VIN_SCHEMA_ID PROVIDED WILL YIELD 0 RESULTS
+ // // WE SHOULD ATLEAST TRY ANY OTHER VIN_SCHEMA_ID THAT HAVE THE
+ // // SAME ORGID
+
+ // // retry for every possible vin_schema_id
+ // for possible_vin_schema_id in possible_vin_schema_ids {
+ // if let Ok(row) = self.query_pattern(possible_vin_schema_id, element_id, key, false) {
+ // return Ok(row);
+ // }
+ // }
+
+ Err(Error::NoResultsFound("query pattern"))
}
pub(crate) fn query_vspec_pattern(
@@ -259,10 +315,6 @@ impl VIN {
Ok(id) => id,
Err(_) => return -1,
};
- let vin_schema_id = match self.get_vin_schema_id() {
- Ok(id) => id,
- Err(_) => return -1,
- };
let con = match self.vpic_connection() {
Ok(c) => c,
Err(_) => return -1,
@@ -278,6 +330,28 @@ impl VIN {
return -1;
}
+ // Check if vspec_schema_id yields only one row.
+ // If so, this is the only option, return it.
+ let mut rows = 0;
+ let mut last_pattern_id = 0;
+ while let Ok(State::Row) = statement.next() {
+ rows+=1;
+ if rows > 1 {
+ break;
+ } else {
+ last_pattern_id = match statement.read::("Id") {
+ Ok(id) => id,
+ Err(_) => continue,
+ };
+ }
+ }
+
+ if rows == 1 {
+ return last_pattern_id;
+ } else {
+ statement.reset().ok();
+ }
+
while let Ok(State::Row) = statement.next() {
let pattern_id = match statement.read::("Id") {
Ok(id) => id,
@@ -310,7 +384,7 @@ impl VIN {
// query pattern with schema id and key_element_id
// compare key_attribute to the attribute from schema_id
if let Ok(element_id) = ElementId::try_from(key_element_id as u16) {
- if let Ok(data) = self.query_pattern(vin_schema_id, element_id, vin_key) {
+ if let Ok(data) = self.query_pattern(element_id, vin_key) {
if data.4 == key_attribute {
return pattern_id;
}
diff --git a/src/vin/schema.rs b/backend/src/vin/schema.rs
similarity index 82%
rename from src/vin/schema.rs
rename to backend/src/vin/schema.rs
index 2a848a5..cef1409 100644
--- a/src/vin/schema.rs
+++ b/backend/src/vin/schema.rs
@@ -70,6 +70,7 @@ impl VIN {
};
if matched_schema_ids.contains(&schema_id) {
+ println!("contains");
return schema_id;
}
}
@@ -86,12 +87,41 @@ impl VIN {
pub fn get_model_id(&self) -> Result {
let key = self.as_key();
- let vin_schema_id = self.get_vin_schema_id()?;
- let data = self.query_pattern(vin_schema_id, ElementId::VehicleModel, key)?;
+ let data = self.query_pattern(ElementId::VehicleModel, key)?;
data.4.parse().map_err(|_| Error::ParseError)
}
+ /// Similiar means they have the same OrgId as each other
+ pub fn get_similiar_vin_schema_ids(&self) -> Result, Error> {
+ let org_id = self.get_organization_id()?;
+ let wmi_id = self.get_wmi_id()?;
+
+ let con = self.vpic_connection()?;
+ let query = "SELECT * FROM Wmi_VinSchema WHERE WmiId = ? and OrgId = ?";
+ let mut statement = con
+ .prepare(query)
+ .map_err(|_| Error::VPICQueryError(query))?;
+
+ statement
+ .bind((1, wmi_id))
+ .map_err(|_| Error::VPICQueryError(query))?;
+
+ statement
+ .bind((2, org_id))
+ .map_err(|_| Error::VPICQueryError(query))?;
+
+ let mut schema_ids = Vec::new();
+ while let Ok(State::Row) = statement.next() {
+ let vin_schema_id = statement
+ .read::("VinSchemaId")
+ .map_err(|_| Error::VPICQueryError(query))?;
+ schema_ids.push(vin_schema_id);
+ }
+
+ Ok(schema_ids)
+ }
+
pub fn get_vspec_schema_id(&self) -> Result {
// query VehicleSpecSchema with MakeId
// save all rows 'Id'
@@ -103,6 +133,7 @@ impl VIN {
let con = self.vpic_connection()?;
let make_id = self.get_make_id()?;
let model_id = self.get_model_id()?;
+ println!("model id: {}", model_id);
let mut matched_spec_schema_ids = Vec::new();
let vspec_schema_id = *self.vspec_schema_id.get_or_init(|| {
diff --git a/src/vin/wmi.rs b/backend/src/vin/wmi.rs
similarity index 96%
rename from src/vin/wmi.rs
rename to backend/src/vin/wmi.rs
index 6c16c49..3b0f4f7 100644
--- a/src/vin/wmi.rs
+++ b/backend/src/vin/wmi.rs
@@ -101,15 +101,15 @@ impl VIN {
}
pub fn get_make_id(&self) -> Result {
- let wmi = self.get_wmi();
+ let wmi_id = self.get_wmi_id()?;
let con = self.vpic_connection()?;
- let query = "SELECT MakeId FROM Wmi WHERE Wmi = ?";
+ let query = "SELECT MakeId FROM Wmi_Make WHERE WmiId = ?";
let mut statement = con
.prepare(query)
.map_err(|_| Error::VPICQueryError(query))?;
statement
- .bind((1, wmi))
+ .bind((1, wmi_id))
.map_err(|_| Error::VPICQueryError(query))?;
match statement.next() {
diff --git a/backend/tauri.conf.json b/backend/tauri.conf.json
new file mode 100644
index 0000000..cf00b5c
--- /dev/null
+++ b/backend/tauri.conf.json
@@ -0,0 +1,45 @@
+{
+ "$schema": "https://schema.tauri.app/config/1",
+ "build": {
+ "devPath": "../frontend",
+ "distDir": "../frontend",
+ "withGlobalTauri": true
+ },
+ "package": {
+ "productName": "obdium",
+ "version": "0.1.0"
+ },
+ "tauri": {
+ "allowlist": {
+ "all": false,
+ "shell": {
+ "all": false,
+ "open": true
+ }
+ },
+ "windows": [
+ {
+ "title": "OBDium",
+ "height": 700,
+ "width": 1200,
+ "minHeight": 400,
+ "minWidth": 1025
+ }
+ ],
+ "security": {
+ "csp": null
+ },
+ "bundle": {
+ "active": true,
+ "targets": "all",
+ "identifier": "com.obdium.app",
+ "icon": [
+ "icons/32x32.png",
+ "icons/128x128.png",
+ "icons/128x128@2x.png",
+ "icons/icon.icns",
+ "icons/icon.ico"
+ ]
+ }
+ }
+}
diff --git a/data/requests.json b/data/requests.json
deleted file mode 100644
index d7a7100..0000000
--- a/data/requests.json
+++ /dev/null
@@ -1,554 +0,0 @@
-[
- {
- "played": false,
- "request": "ATZ",
- "request_type": "ATCommand",
- "response": "\r\rELM327 v1.5\r\r"
- },
- {
- "played": false,
- "request": "ATE0",
- "request_type": "ATCommand",
- "response": "ATE0\rOK\r\r"
- },
- {
- "played": false,
- "request": "ATL0",
- "request_type": "ATCommand",
- "response": "OK\r\r"
- },
- {
- "played": false,
- "request": "ATH1",
- "request_type": "ATCommand",
- "response": "OK\r\r"
- },
- {
- "played": false,
- "request": "ATSP0",
- "request_type": "ATCommand",
- "response": "OK\r\r"
- },
- {
- "played": true,
- "request": "0100",
- "request_type": "PIDCommand",
- "response": "\n7EA 06 41 00 98 3A 80 13 \n7E8 06 41 00 BE 3F A8 13 \n\n"
- },
- {
- "played": true,
- "request": "0120",
- "request_type": "PIDCommand",
- "response": "7EA 06 41 20 80 01 A0 01 \n7E8 06 41 20 90 15 B0 15 \n\n"
- },
- {
- "played": true,
- "request": "0140",
- "request_type": "PIDCommand",
- "response": "7EA 06 41 40 44 CC 00 21 \n7E8 06 41 40 7A 1C 80 00 \n\n"
- },
- {
- "played": true,
- "request": "0160",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0180",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "01A0",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "01C0",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0101",
- "request_type": "PIDCommand",
- "response": "7E8 06 41 01 00 07 A1 00 \n\n"
- },
- {
- "played": true,
- "request": "0101",
- "request_type": "PIDCommand",
- "response": "7E8 06 41 01 00 07 A1 00 \n\n"
- },
- {
- "played": true,
- "request": "0101",
- "request_type": "PIDCommand",
- "response": "7E8 06 41 01 00 07 A1 00 \n\n"
- },
- {
- "played": true,
- "request": "011C",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 1C 06 \n\n"
- },
- {
- "played": true,
- "request": "011E",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0142",
- "request_type": "PIDCommand",
- "response": "7E8 04 41 42 39 C1 \n\n"
- },
- {
- "played": true,
- "request": "0121",
- "request_type": "PIDCommand",
- "response": "7E8 04 41 21 00 00 \n\n"
- },
- {
- "played": true,
- "request": "014D",
- "request_type": "PIDCommand",
- "response": "7E8 04 41 4D 00 00 \n\n"
- },
- {
- "played": true,
- "request": "014E",
- "request_type": "PIDCommand",
- "response": "7E8 04 41 4E 00 5C \n\n"
- },
- {
- "played": true,
- "request": "0130",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 30 04 \n\n"
- },
- {
- "played": true,
- "request": "AT DP",
- "request_type": "ATCommand",
- "response": "AUTO, ISO 15765-4 (CAN 11/500) \r\r"
- },
- {
- "played": true,
- "request": "0101",
- "request_type": "PIDCommand",
- "response": "7E8 06 41 01 00 07 A1 00 \n\n"
- },
- {
- "played": true,
- "request": "0101",
- "request_type": "PIDCommand",
- "response": "7E8 06 41 01 00 07 A1 00 \n\n"
- },
- {
- "played": true,
- "request": "0101",
- "request_type": "PIDCommand",
- "response": "7E8 06 41 01 00 07 A1 00 \n\n"
- },
- {
- "played": true,
- "request": "0101",
- "request_type": "PIDCommand",
- "response": "7E8 06 41 01 00 07 A1 00 \n\n"
- },
- {
- "played": true,
- "request": "010C",
- "request_type": "PIDCommand",
- "response": "7E8 04 41 0C 00 00 \n\n"
- },
- {
- "played": true,
- "request": "0104",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 04 57 \n\n"
- },
- {
- "played": true,
- "request": "0167",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0105",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 05 44 \n\n"
- },
- {
- "played": true,
- "request": "019D",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0101",
- "request_type": "PIDCommand",
- "response": "7E8 06 41 01 00 07 A1 00 \n\n"
- },
- {
- "played": true,
- "request": "011F",
- "request_type": "PIDCommand",
- "response": "7E8 04 41 1F 00 26 \n\n"
- },
- {
- "played": true,
- "request": "017F",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "01A6",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0167",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "015C",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "221154",
- "request_type": "Arbitrary",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "221470",
- "request_type": "Arbitrary",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0161",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0162",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0163",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0164",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0106",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 06 80 \n\n"
- },
- {
- "played": true,
- "request": "0108",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0107",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 07 79 \n\n"
- },
- {
- "played": true,
- "request": "0109",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0103",
- "request_type": "PIDCommand",
- "response": "7E8 04 41 03 04 00 \n\n"
- },
- {
- "played": true,
- "request": "010A",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "012F",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0122",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0123",
- "request_type": "PIDCommand",
- "response": "7E8 04 41 23 1A 0E \n\n"
- },
- {
- "played": true,
- "request": "0151",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 51 01 \n\n"
- },
- {
- "played": true,
- "request": "0152",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "015D",
- "request_type": "PIDCommand",
- "response": "7E8 04 41 5D 66 00 \n\n"
- },
- {
- "played": true,
- "request": "012E",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 2E 00 \n\n"
- },
- {
- "played": true,
- "request": "0132",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "01A2",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "010D",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 0D 37 \n\n"
- },
- {
- "played": true,
- "request": "010E",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 0E 75 \n\n"
- },
- {
- "played": true,
- "request": "0111",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 11 70 \n\n"
- },
- {
- "played": true,
- "request": "0145",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 45 19 \n\n"
- },
- {
- "played": true,
- "request": "0147",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 47 92 \n\n"
- },
- {
- "played": true,
- "request": "0148",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0149",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 49 00 \n\n"
- },
- {
- "played": true,
- "request": "014A",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 4A 45 \n\n"
- },
- {
- "played": true,
- "request": "014B",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0114",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0124",
- "request_type": "PIDCommand",
- "response": "7E8 06 41 24 7B A8 65 D9 \n\n"
- },
- {
- "played": true,
- "request": "0115",
- "request_type": "PIDCommand",
- "response": "7E8 04 41 15 07 FF \n\n"
- },
- {
- "played": true,
- "request": "0125",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0116",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0126",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0117",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0127",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0118",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0128",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0119",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0129",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "011A",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "012A",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "011B",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "012B",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0166",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "014F",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "010F",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 0F 3C \n\n"
- },
- {
- "played": true,
- "request": "0110",
- "request_type": "PIDCommand",
- "response": "7E8 04 41 10 01 46 \n\n"
- },
- {
- "played": true,
- "request": "0146",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 46 43 \n\n"
- },
- {
- "played": true,
- "request": "0150",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0112",
- "request_type": "PIDCommand",
- "response": "NO DATA"
- },
- {
- "played": true,
- "request": "0133",
- "request_type": "PIDCommand",
- "response": "7E8 03 41 33 61 \n\n"
- }
-]
\ No newline at end of file
diff --git a/docs/app_image.png b/docs/app_image.png
new file mode 100644
index 0000000..1cc2027
Binary files /dev/null and b/docs/app_image.png differ
diff --git a/examples/vin-decoding.rs b/examples/vin-decoding.rs
index 8e32471..b1f1a22 100644
--- a/examples/vin-decoding.rs
+++ b/examples/vin-decoding.rs
@@ -25,7 +25,7 @@ fn main() {
// Used internally as well
let vspec_schema_id = vin.get_vspec_schema_id().unwrap();
let vspec_pattern_id = vin.get_vspec_pattern_id().unwrap();
-
+
println!("Model year: {}", model_year);
println!("WMI: {}", wmi);
println!("WMI ID: {}", wmi_id);
diff --git a/frontend/assets/icons/arrow-icon.png b/frontend/assets/icons/arrow-icon.png
new file mode 100644
index 0000000..37ca481
Binary files /dev/null and b/frontend/assets/icons/arrow-icon.png differ
diff --git a/frontend/assets/icons/connected.png b/frontend/assets/icons/connected.png
new file mode 100644
index 0000000..533f813
Binary files /dev/null and b/frontend/assets/icons/connected.png differ
diff --git a/frontend/assets/icons/edit.png b/frontend/assets/icons/edit.png
new file mode 100644
index 0000000..1dbd61d
Binary files /dev/null and b/frontend/assets/icons/edit.png differ
diff --git a/frontend/assets/icons/graphs.png b/frontend/assets/icons/graphs.png
new file mode 100644
index 0000000..fb2aad0
Binary files /dev/null and b/frontend/assets/icons/graphs.png differ
diff --git a/frontend/assets/icons/not-connected.png b/frontend/assets/icons/not-connected.png
new file mode 100644
index 0000000..75c66aa
Binary files /dev/null and b/frontend/assets/icons/not-connected.png differ
diff --git a/frontend/assets/icons/search.png b/frontend/assets/icons/search.png
new file mode 100644
index 0000000..2d6952b
Binary files /dev/null and b/frontend/assets/icons/search.png differ
diff --git a/frontend/assets/icons/settings.png b/frontend/assets/icons/settings.png
new file mode 100644
index 0000000..e55f716
Binary files /dev/null and b/frontend/assets/icons/settings.png differ
diff --git a/frontend/assets/icons/start.png b/frontend/assets/icons/start.png
new file mode 100644
index 0000000..4de9166
Binary files /dev/null and b/frontend/assets/icons/start.png differ
diff --git a/frontend/assets/icons/table.png b/frontend/assets/icons/table.png
new file mode 100644
index 0000000..af24a46
Binary files /dev/null and b/frontend/assets/icons/table.png differ
diff --git a/frontend/assets/icons/vin-decoding.png b/frontend/assets/icons/vin-decoding.png
new file mode 100644
index 0000000..612cc9f
Binary files /dev/null and b/frontend/assets/icons/vin-decoding.png differ
diff --git a/frontend/assets/images/vehicle.png b/frontend/assets/images/vehicle.png
new file mode 100644
index 0000000..7cc8205
Binary files /dev/null and b/frontend/assets/images/vehicle.png differ
diff --git a/frontend/components/buttons.css b/frontend/components/buttons.css
new file mode 100644
index 0000000..f95f57d
--- /dev/null
+++ b/frontend/components/buttons.css
@@ -0,0 +1,207 @@
+/* Regular button */
+
+.button-row {
+ display: flex;
+ gap: 8px;
+}
+
+.btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: none;
+ color: #F7F3FF;
+ width: 120px;
+ height: 30px;
+ margin-top: 10px;
+ font-size: 15px;
+ font-weight: 700;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background 0.2s;
+ text-transform: uppercase;
+ vertical-align: middle;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
+}
+
+.btn.connect {
+ background-color: #2A2A2A;
+}
+
+.btn.disconnect {
+ background-color: #202020;
+ width: 162px;
+}
+
+.btn:hover {
+ background-color: #1d1d1d;
+ box-shadow: 0 4px 20px #1d1d1d;
+}
+
+.btn:disabled {
+ background-color: #252525;
+ color: #aaa;
+ cursor: not-allowed;
+ box-shadow: none;
+ opacity: 0.6;
+}
+
+/* Custom dropdown */
+
+.form-row {
+ display: flex;
+ flex-wrap: wrap;
+}
+
+.dropdown {
+ position: relative;
+ display: inline-block;
+ width: 290px;
+ margin-right: 10px;
+}
+
+.dropdown label {
+ margin-left: 1.5px;
+ font-size: 18px;
+ font-weight: 700;
+ text-transform: uppercase;
+ color: #f3f3f2;
+ margin-bottom: 5px;
+ display: block;
+}
+
+.dropdown-toggle {
+ font-family: 'Montserrat', sans-serif;
+ width: 100%;
+ padding-top: 10px;
+ padding-left: 11px;
+ background-color: #151515;
+ border: none;
+ color: #49454F;
+ font-weight: 600;
+ font-size: 14px;
+ cursor: pointer;
+ text-align: left;
+ border-radius: 4px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
+}
+
+.dropdown-toggle::after {
+ content: url('data:image/svg+xml;utf8, ');
+ float: right;
+ font-size: 0.5em;
+ margin-bottom: 4px;
+ padding-right: 5px;
+}
+
+.dropdown-menu {
+ list-style: none;
+ margin: 5px 0 0 0;
+ position: absolute;
+ width: 100%;
+ background-color: #151515;
+ border-radius: 0 0 5px 5px;
+ display: none;
+ z-index: 100;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
+}
+
+.dropdown-menu li {
+ padding: 8px;
+ padding-left: 11px;
+ font-weight: 600;
+ font-size: 14px;
+ cursor: pointer;
+ color: #615c68;
+ border-radius: 0 0 5px 5px;
+}
+
+.dropdown-menu li:hover {
+ background-color: #0F0F0F;
+}
+
+.arrow-icon {
+ width: 32px;
+ height: 32px;
+ padding: 0;
+ border: none;
+ background: none;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: transform 0.3s ease;
+}
+
+.settings-dropdown-column {
+ display: flex;
+ flex-direction: column;
+}
+
+/* Switch */
+
+.switch {
+ position: relative;
+ width: 42px;
+ height: 20px;
+ display: flex;
+ white-space: nowrap;
+ margin-bottom: 16px;
+ margin-top: -8px;
+}
+
+.switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+.settings-label {
+ color: #49454F;
+ font-weight: 600;
+ font-size: 1rem;
+ display: flex;
+ align-items: center;
+}
+
+.slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #2b2b2b;
+ transition: 0.4s;
+ border-radius: 34px;
+}
+
+.slider::before {
+ position: absolute;
+ content: "";
+ height: 15px;
+ width: 15px;
+ left: 3px;
+ bottom: 3px;
+ background-color: #f3f3f2;
+ transition: 0.4s;
+ border-radius: 50%;
+}
+
+input:checked + .slider {
+ background-color: #1a69ae;
+}
+
+input:checked+.slider::before {
+ transform: translateX(22px);
+}
+
+input:disabled + .slider {
+ background-color: #2b2b2b;
+ cursor: not-allowed;
+ opacity: 0.6;
+}
+
+input:disabled + .slider::before {
+ background-color: #424242;
+}
\ No newline at end of file
diff --git a/frontend/components/card.css b/frontend/components/card.css
new file mode 100644
index 0000000..e6c746f
--- /dev/null
+++ b/frontend/components/card.css
@@ -0,0 +1,48 @@
+/*
+ "Card" that holds OBD information:
+ A label, value and unit
+*/
+
+.card {
+ background-color: #151515;
+ border-radius: 12px;
+ padding: 28px 25px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ gap: 8px;
+ user-select: none;
+}
+
+.card:hover {
+ background-color: #1d1d1d;
+ box-shadow: 0 4px 20px #1d1d1d;
+}
+
+.card h3 {
+ font-size: 0.97rem;
+ font-weight: 500;
+ text-transform: uppercase;
+ margin: 0;
+ line-height: 1;
+ color: #D4DADF;
+}
+
+.card .value {
+ font-size: 3.4rem;
+ font-weight: 700;
+ margin: 0;
+ line-height: 1;
+ color: #F7F3FF;
+}
+
+.card .unit {
+ font-size: 1.3rem;
+ font-weight: 200;
+ color: #D4DADF;
+}
+
+.card.dimmed * {
+ opacity: 0.15 !important;
+}
\ No newline at end of file
diff --git a/frontend/components/forms.css b/frontend/components/forms.css
new file mode 100644
index 0000000..87a5020
--- /dev/null
+++ b/frontend/components/forms.css
@@ -0,0 +1,71 @@
+/* Simple search bars */
+
+.search-container {
+ position: relative;
+ width: 50%;
+ display: flex;
+ align-items: center;
+ background-color: #151515;
+ border-radius: 10px;
+}
+
+.search {
+ position: relative;
+ font-size: .95rem;
+ background-color: #151515;
+ border-radius: 10px;
+ padding: 6px 20px;
+ width: 100%;
+ height: 35px;
+ border: none;
+ outline: none;
+ color: #F7F3FF;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
+}
+
+.search-icon {
+ position: absolute;
+ right: 15px;
+ width: 16px;
+ height: 16px;
+ pointer-events: none;
+}
+
+.search::placeholder {
+ color: #49454F;
+ opacity: 1;
+}
+
+.vin-input-container {
+ position: relative;
+ width: 58%;
+ display: flex;
+ align-items: center;
+ background-color: #151515;
+ border-radius: 5px;
+}
+
+.vin-input {
+ position: relative;
+ font-size: .95rem;
+ background-color: #171717;
+ border-radius: 5px;
+ margin-top: 2px;
+
+ padding: 6px 20px;
+ width: 100%;
+ height: 35px;
+ border: none;
+ outline: none;
+ color: #F7F3FF;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4);
+}
+
+.vin-input::placeholder {
+ color: #49454F;
+ opacity: 1;
+}
+
+input[type="file"] {
+ display: none;
+}
\ No newline at end of file
diff --git a/frontend/components/pid.css b/frontend/components/pid.css
new file mode 100644
index 0000000..3941eb4
--- /dev/null
+++ b/frontend/components/pid.css
@@ -0,0 +1,128 @@
+/*
+ A row containing a title and dropdown arrow.
+ On click, this row expands to show pid details.
+ "Glows" when hovered.
+*/
+
+.pid-container:hover .pid-row,
+.pid-container:hover .pid-details {
+ background-color: #1d1d1d;
+ box-shadow: 0 4px 20px #1d1d1d;
+}
+
+.pid-row {
+ background-color: #151515;
+ height: 10px;
+ padding: 19px 10px;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ margin-bottom: 0px;
+}
+
+.pid-row .name {
+ font-size: 1.0rem;
+ font-weight: 550;
+ padding-left: 10px;
+ color: #f3f3f2;
+}
+
+.pid-row.expanded {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ box-shadow: none;
+}
+
+.pid-row.expanded .arrow-icon {
+ transform: rotate(-180deg);
+}
+
+.pid-row.expanded+.pid-details {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+
+.pid-details {
+ height: 0;
+ opacity: 0;
+ overflow: hidden;
+ background-color: #151515;
+ margin-top: 0px;
+
+ transform: translateY(-10px);
+ font-size: 0.95rem;
+ position: static;
+ padding: 0 12px;
+ transition: height 0.3s ease, opacity 0.3s ease, transform 0.3s ease;
+}
+
+.pid-data-columns {
+ display: flex;
+ gap: 40px;
+}
+
+.pid-column {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+
+.pid-button {
+ margin-top: 12px;
+ margin-bottom: 10px;
+ height: 22px;
+ width: 85px;
+ background-color: #303030;
+ color: #F7F3FF;
+ font-weight: 700;
+ font-size: 0.8em;
+ border: none;
+ padding-left: 8px;
+ padding-right: 8px;
+ border-radius: 5px;
+ cursor: pointer;
+}
+
+.pid-label {
+ font-weight: 600;
+ font-size: 0.9rem;
+ color: #F7F3FF;
+ text-transform: uppercase;
+}
+
+.pid-value {
+ font-weight: 500;
+ font-size: 0.85rem;
+ color: #F7F3FF;
+ white-space: nowrap;
+}
+
+
+.pid-row-scroll {
+ overflow-y: auto;
+ flex-grow: 1;
+ padding-right: 10px;
+ padding-bottom: 50px;
+ height: 100%;
+ min-height: 0;
+}
+
+.pid-row-scroll::-webkit-scrollbar {
+ width: 13px;
+}
+
+.pid-row-scroll::-webkit-scrollbar-track {
+ background: #0F0F0F;
+ border-radius: 10px;
+}
+
+.pid-row-scroll::-webkit-scrollbar-thumb {
+ background-color: #29272c;
+ border-radius: 10px;
+ border: 2px solid #0F0F0F;
+}
+
+.pid-row-scroll::-webkit-scrollbar-thumb:hover {
+ background-color: #49454F;
+}
\ No newline at end of file
diff --git a/frontend/components/sidebar.css b/frontend/components/sidebar.css
new file mode 100644
index 0000000..92c2ba9
--- /dev/null
+++ b/frontend/components/sidebar.css
@@ -0,0 +1,61 @@
+/* VSCode like sidebar */
+
+.sidebar-button {
+ width: 45px;
+ height: 45px;
+ background-color: transparent;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+}
+
+.sidebar-button-img {
+ margin-left: 3px;
+ color: #49454F;
+}
+
+.sidebar-button::after {
+ content: attr(data-tooltip);
+ position: absolute;
+ left: 50px;
+ background: #151515;
+ color: #eee;
+ font-size: 12px;
+ padding: 4px 8px;
+ border-radius: 4px;
+ white-space: nowrap;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.2s ease 0s;
+ top: 50%;
+ transform: translateY(-50%);
+ z-index: 1000;
+}
+
+.sidebar-button.active-button::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ width: 3px;
+ background-color: #f3f3f2;
+ font-family: 'Montserrat', sans-serif;
+}
+
+.sidebar-button:hover::after {
+ opacity: 1;
+ transition-delay: 0.3s;
+}
+
+.sidebar-button:hover svg {
+ stroke: #f3f3f2;
+}
+
+.sidebar-button.active-button svg {
+ stroke: #f3f3f2;
+}
\ No newline at end of file
diff --git a/frontend/components/vin.css b/frontend/components/vin.css
new file mode 100644
index 0000000..52f8e10
--- /dev/null
+++ b/frontend/components/vin.css
@@ -0,0 +1,44 @@
+/* Vin that is apart of the header in all screens */
+
+.vin {
+ font-size: 16px;
+ color: #BDBDBD;
+ margin-left: 1px;
+ font-weight: 500;
+}
+
+/* Components of the vin screen */
+
+.car-image {
+ height: 215px;
+}
+
+.vin-results {
+ overflow-y: auto;
+ display: flex;
+ gap: 70px;
+ margin-top: 20px;
+ flex-wrap: wrap;
+ height: 100%;
+}
+
+.vin-column {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.vin-label .label {
+ font-size: 16px;
+ color: #D4DADF;
+ font-weight: 500;
+ text-transform: uppercase;
+}
+
+.vin-label .value {
+ font-size: 20px;
+ font-weight: 700;
+ color: #F7F3FF;
+ margin-bottom: -7px;
+ max-width: 400px;
+}
\ No newline at end of file
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 0000000..b282b1c
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,645 @@
+
+
+
+
+
+
+ OBDII Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NO CONNECTION ESTABLISHED
+
+
+
+
+
+
+ CONNECT
+
+
+ DISCONNECT
+
+
+
+
+
+
+
+
+
+ CHOOSE SERIAL PORT, BAUD RATE AND PROTOCOL
+
+
+
+
+
+
Graphs
+
+
+
+
+
+
+
VEHICLE IDENTIFICATION NUMBER
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SEMI-AUTO HEADLAMP BEAM SWITCHING
+
+
+
+
DYNAMIC BRAKE SUPPORT
+
+
+
+
AIRBAG LOCATIONS: KNEE
+
+
+
+
AIRBAG LOCATIONS: SIDE
+
+
+
+
+
+
+
+
+
ENGINE MANUFACTURER
+
+
+
+
ANTI-LOCK BRAKING SYSTEM
+
+
+
+
+
+
+
+
+
DAYTIME RUNNING LIGHT
+
+
+
+
WINDOW AUTO-REVERSE
+
+
+
+
AIRBAG LOCATIONS: FRONT
+
+
+
+
+
+
+
+
+
AUTOMATIC CRASH NOTIFICAITON
+
+
+
+
+
TRANSMISSION SPEEDS
+
+
+
+
+
+
+
+
ENGINE DISPLACEMENT
+
+
+
+
GROSS VEHICLE WEIGHT RATING
+
+
+
+
AIRBAG LOCATIONS: CURTAIN
+
+
+
+
VEHICLE MANUFACTURER
+
+
+
+
+
+
+
+
+
+
+
LOGGING
+
+
+ RECORD OBD RESPONSES
+
+
+
+
+ REPLAY SAVED RESPONSES
+
+
+
+
+
+
+ CHOOSE A FILE
+
+ LOG FILE PATH
+
+
+
+
+
+
+
+
+
+
UNIT CONVERSION
+
+
+
SPEED
+
KMH (default)
+
+
+
+
DISTANCE
+
KM (default)
+
+
+
+
TEMPERATURE
+
°C (default)
+
+
+
+
ENERGY
+
NM (default)
+
+
+
+
PRESSURE
+
KPA (default)
+
+
+
+
FLOW RATE
+
L/H (default)
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/layout.css b/frontend/layout.css
new file mode 100644
index 0000000..2644239
--- /dev/null
+++ b/frontend/layout.css
@@ -0,0 +1,83 @@
+.main {
+ flex: 1;
+ padding: 20px 15px;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ min-height: 0;
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+}
+
+.header .info {
+ font-size: 1.2rem;
+}
+
+.container {
+ max-width: 1000px;
+ margin: auto;
+ background: none !important;
+ background-color: transparent !important;
+}
+
+.sidebar {
+ width: 60px;
+ background-color: #0F0F0F;
+ padding: 10px 0;
+ gap: 5px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 15px;
+ overflow-y: auto;
+ flex: 1;
+ align-content: start;
+ min-height: 0;
+}
+
+.screen {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ min-height: 0;
+}
+
+.screen-label {
+ padding-bottom: 11px;
+ font-size: 1.5rem;
+ color: #F7F3FF;
+ font-weight: 700;
+ margin-left: 1px;
+}
+
+#connection-icon {
+ width: 1em;
+ height: 1em;
+ object-fit: contain;
+}
+
+#edit {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ max-height: 100vh;
+ min-height: 0;
+}
+
+#dashboard {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+}
diff --git a/frontend/scripts/events.js b/frontend/scripts/events.js
new file mode 100644
index 0000000..9f1ab42
--- /dev/null
+++ b/frontend/scripts/events.js
@@ -0,0 +1,209 @@
+const { listen, emit } = window.__TAURI__.event;
+
+listen('update-card', (event) => {
+ const cards = document.querySelectorAll('.card');
+ const exists = Array.from(cards).some(card => {
+ return card.textContent.includes(event.payload.name);
+ });
+
+ // create card if it doesnt exist
+ if (!exists) {
+ const container = document.querySelector('.grid');
+ if (!container)
+ return;
+
+ // create the card
+ // has a header, value, and then unit
+ const card = document.createElement('div');
+ const h3 = document.createElement('h3');
+ const valueDiv = document.createElement('div');
+ const unitSpan = document.createElement('span');
+
+ card.className = 'card';
+ valueDiv.className = 'value';
+ unitSpan.className = 'unit';
+
+ h3.textContent = event.payload.name;
+
+ if (event.payload.unit.toLowerCase() !== "no data")
+ unitSpan.textContent = event.payload.unit;
+
+ // if the unit is no data meaning that scalar has 'no data'
+ // (see backend docs), then display 'N/A' for value
+ const valueText = event.payload.unit.toLowerCase() === "no data" ? "N/A" : event.payload.value.toString();
+
+ // add value and unit to one div to align horizontally
+ valueDiv.appendChild(document.createTextNode(valueText + " "));
+ valueDiv.appendChild(unitSpan);
+
+ // append elements to card
+ card.appendChild(h3);
+ card.appendChild(valueDiv);
+
+ container.appendChild(card);
+
+ return;
+ }
+
+ cards.forEach(card => {
+ // Don't update card data.
+ if (card.classList.contains('dimmed')) {
+ return;
+ }
+
+ const h3 = card.querySelector('h3');
+ if (!h3) return;
+
+ const title = h3.textContent?.toLowerCase();
+ if (title == event.payload.name.toLowerCase()) {
+ const valueElem = card.querySelector('.value');
+ const unitElem = card.querySelector('.unit');
+
+ if (valueElem) {
+ const textNode = Array.from(valueElem.childNodes).find(
+ node => node.nodeType === Node.TEXT_NODE
+ );
+
+ if (textNode) {
+ textNode.textContent = event.payload.unit.toLowerCase() === "no data"
+ ? "N/A "
+ : event.payload.value.toString() + " ";
+ }
+ }
+ }
+ });
+});
+
+listen('vehicle-details', (event) => {
+ const vin = document.querySelector(".vin")
+ const makeModel = document.querySelector(".car-model");
+
+ if (!hideVin) {
+ vin.textContent = "VIN: " + event.payload.vin.toUpperCase();
+ } else {
+ vin.textContent = "VIN: " + event.payload.vin.toUpperCase().slice(0, 6) + "*".repeat(12);
+ }
+
+ makeModel.textContent = (event.payload.make + " " + event.payload.model).toUpperCase();
+});
+
+listen('connection-status', (event) => {
+ const connection_label = document.getElementById("connection-label");
+ const connection_icon = document.getElementById("connection-icon");
+
+ if (event.payload.connected) {
+ connection_label.textContent = "ELM327 CONNECTED VIA " + event.payload.serial_port.toUpperCase();
+ connection_icon.src = "/assets/icons/connected.png";
+ window.connected = true;
+ } else {
+ connection_label.textContent = "ELM327 NOT CONNECTED";
+ connection_icon.src = "/assets/icons/not-connected.png";
+ window.connected = false;
+ }
+
+
+ console.log(event.payload.message);
+});
+
+listen('update-pids', (event) => {
+ const pids = event.payload;
+ const pidList = document.getElementById('pid-list');
+
+ for (const pidInfo of pids) {
+ const pidGroup = document.createElement('div');
+ pidGroup.className = 'pid-group';
+
+ pidGroup.innerHTML = `
+
+
+
+
${pidInfo.pid_name.toUpperCase()}
+
+
+
+
+
MODE
+
$${pidInfo.mode}
+
+
+
+
COMMAND
+
${pidInfo.mode + pidInfo.pid}
+
+
+
EQUATION
+
${pidInfo.formula == "" ? "??" : pidInfo.formula}
+
+
+
UNIT
+
${pidInfo.unit == "" ? "??" : pidInfo.unit.toUpperCase()}
+
+
+
DETAILS
+
+
+ `;
+
+ pidList.appendChild(pidGroup);
+
+ // Increment results counter
+ const header = document.getElementById('pid-list-header');
+ header.textContent = "VIEW PIDS (" + pidList.children.length + ")";
+
+ // Add expand/collapse event listener
+ const row = pidGroup.querySelector('.pid-row');
+ const details = pidGroup.querySelector('.pid-details');
+
+ row.addEventListener('click', () => {
+ const expanded = row.classList.contains('expanded');
+
+ if (expanded) {
+ details.style.height = details.scrollHeight + 'px';
+ requestAnimationFrame(() => {
+ details.style.height = '0px';
+ });
+ row.classList.remove('expanded');
+ } else {
+ details.style.display = 'block';
+ const height = details.scrollHeight + 'px';
+ details.style.height = '0px';
+ requestAnimationFrame(() => {
+ details.style.height = height;
+ });
+ row.classList.add('expanded');
+ }
+
+ details.addEventListener('transitionend', function handler(e) {
+ if (e.propertyName === 'height') {
+ if (!row.classList.contains('expanded')) {
+ details.style.display = 'none';
+ } else {
+ details.style.height = 'auto';
+ }
+ details.removeEventListener('transitionend', handler);
+ }
+ });
+ });
+ }
+});
+
+const menu = document.getElementById('serial-port-dropdown-menu');
+const serialPortSelected = document.getElementById('serial-port-selected');
+
+listen('update-serial-ports', (event) => {
+ serialPortSelected.textContent = "NO PORTS SELECTED";
+ menu.innerHTML = '';
+
+ if (event.payload === "") {
+ return;
+ }
+
+ const portOption = document.createElement('li');
+ portOption.textContent = event.payload;
+ portOption.dataset.value = event.payload;
+
+ menu.appendChild(portOption);
+})
\ No newline at end of file
diff --git a/frontend/scripts/main.js b/frontend/scripts/main.js
new file mode 100644
index 0000000..54a992b
--- /dev/null
+++ b/frontend/scripts/main.js
@@ -0,0 +1,148 @@
+const { listen, emit } = window.__TAURI__.event;
+import { saveConnectionConfig } from './settings.js'
+
+// ELM connection
+// Changes when connection-status is fired
+let connected = false;
+window.connectionConfig = {
+ serialPort: "0",
+ baudRate: "0",
+ protocol: 0,
+};
+
+// Personal settings
+let hideVin = false;
+let deleteLogsOnExit = false;
+
+// When frontend gets loaded
+// alert the backend with an event.
+window.addEventListener('DOMContentLoaded', () => {
+ emit('frontend-loaded');
+ // Load pid list
+ emit('get-pids')
+
+ // load serial ports
+ emit('get-serial-ports');
+});
+
+export async function connect_elm(baudRate, serialPort, protocol) {
+ const status = document.getElementById('connection-details');
+ const recordResponses = document.getElementById('record-responses');
+ const replayResponses = document.getElementById('replay-responses');
+
+ let dots = 0;
+ const interval = setInterval(() => {
+ if (dots == 4) {
+ dots = 0;
+ }
+
+ status.textContent = "CONNECTING" + '.'.repeat(dots);
+ dots += 1;
+ }, 500);
+
+ connectButton.disabled = true;
+ emit('connect-elm', { serialPort: serialPort, baudRate: parseInt(baudRate), protocol: parseInt(protocol) });
+ await new Promise(r => setTimeout(r, 1000));
+ clearInterval(interval);
+
+ if (window.connected) {
+ status.textContent = "CONNECTED THROUGH SERIAL PORT " + serialPort.toUpperCase();
+ connectButton.disabled = true;
+ disconnectButton.disabled = false;
+
+ window.connectionConfig = {
+ serialPort: serialPort,
+ baudRate: baudRate,
+ protocol: parseInt(protocol),
+ };
+
+ saveConnectionConfig();
+
+ // enable buttons for logging
+ recordResponses.disabled = false;
+ replayResponses.disabled = false;
+ } else {
+ status.textContent = "FAILED TO CONNECT THROUGH SERIAL PORT";
+ connectButton.disabled = false;
+ disconnectButton.disabled = true;
+ }
+
+ document.getElementById('baud-rate-selected').textContent = baudRate;
+ document.getElementById('serial-port-selected').textContent = serialPort;
+ document.getElementById('protocol-selected').dataset.value = protocol ;
+}
+
+async function disconnect_elm() {
+ const recordResponses = document.getElementById('record-responses');
+ const replayResponses = document.getElementById('replay-responses');
+ const status = document.getElementById('connection-details');
+
+ let dots = 0;
+ const interval = setInterval(() => {
+ if (dots == 4) {
+ dots = 0;
+ }
+
+ status.textContent = "DISCONNECTING" + '.'.repeat(dots);
+ dots += 1;
+ }, 500);
+
+ connectButton.disabled = true;
+ emit('disconnect-elm');
+ await new Promise(r => setTimeout(r, 1000));
+ clearInterval(interval);
+
+ status.textContent = "NO CONNECTION ESTABLISHED";
+ connectButton.disabled = false;
+ disconnectButton.disabled = true;
+ recordResponses.disabled = true;
+ replayResponses.disabled = true;
+}
+
+const dropdowns = document.querySelectorAll(".dropdown");
+
+dropdowns.forEach(dropdown => {
+ const toggle = dropdown.querySelector(".dropdown-toggle");
+ const menu = dropdown.querySelector(".dropdown-menu");
+
+ toggle.addEventListener("click", (e) => {
+ e.stopPropagation();
+ if (menu.style.display === "block") {
+ menu.style.display = "none";
+ } else {
+ document.querySelectorAll(".dropdown-menu").forEach(m => {
+ m.style.display = "none";
+ });
+ menu.style.display = "block";
+ }
+ });
+
+ menu.addEventListener("click", (e) => {
+ if (e.target.tagName === "LI") {
+ toggle.textContent = e.target.textContent;
+ toggle.dataset.value = e.target.dataset.value;
+ menu.style.display = "none";
+ }
+ });
+});
+
+document.addEventListener("click", () => {
+ document.querySelectorAll(".dropdown-menu").forEach(menu => {
+ menu.style.display = "none";
+ });
+});
+
+const connectButton = document.getElementById('btn-connect');
+const disconnectButton = document.getElementById('btn-disconnect');
+
+connectButton.addEventListener("click", async () => {
+ const baudRate = document.getElementById('baud-rate-selected');
+ const serialPort = document.getElementById('serial-port-selected');
+ const protocol = document.getElementById('protocol-selected');
+
+ connect_elm(baudRate.textContent, serialPort.textContent, parseInt(protocol.dataset.value));
+})
+
+disconnectButton.addEventListener("click", async () => {
+ disconnect_elm();
+})
\ No newline at end of file
diff --git a/frontend/scripts/settings.js b/frontend/scripts/settings.js
new file mode 100644
index 0000000..6fe50ca
--- /dev/null
+++ b/frontend/scripts/settings.js
@@ -0,0 +1,136 @@
+import { connect_elm } from './main.js';
+
+function importSettings() {
+ let settings = JSON.parse(localStorage.getItem('userSettings'));
+ if (!settings) {
+ return;
+ }
+
+ console.log("Importing settings:", settings);
+
+ if (!settings.connectionConfig) {
+ settings.connectionConfig = window.connectionConfig;
+ }
+
+ // read from connectionConfig
+ // if empty, we cannot connect
+ if (settings.autoConnect && !window.connected && settings.connectionConfig) {
+ console.log("Auto-connect");
+ connect_elm(settings.connectionConfig.baudRate, settings.connectionConfig.serialPort, 0);
+ }
+
+ window.hideVin = settings.showPartialVin;
+ window.deleteLogsOnExit = settings.deleteLogsOnExit;
+
+ // show buttons depending on which are toggled
+ document.getElementById('save-dtcs').checked = settings.saveDtcs;
+ document.getElementById('auto-check-codes').checked = settings.autoCheckCodes;
+ document.getElementById('auto-connect').checked = settings.autoConnect;
+ document.getElementById('hide-vin').checked = settings.showPartialVin;
+ document.getElementById('del-logs').checked = settings.deleteLogsOnExit;
+ console.log("Imported settings:", settings);
+}
+
+export function saveConnectionConfig() {
+ console.log("Saving connection config!");
+
+ let settings = JSON.parse(localStorage.getItem('userSettings'));
+ if (!settings) {
+ return;
+ }
+
+ if (window.connectionConfig) {
+ console.log("Not null");
+ settings.connectionConfig = window.connectionConfig;
+ localStorage.setItem('userSettings', JSON.stringify(settings));
+ console.log("Saved new settings:", settings);
+ }
+}
+
+function settingChange(event) {
+ // get existing settings
+ let settings = JSON.parse(localStorage.getItem('userSettings'));
+ if (!settings) {
+ settings = {
+ saveDtcs: false,
+ autoCheckCodes: false,
+ autoConnect: false,
+ connectionConfig: {
+ protocol: 0,
+ baudRate: "0",
+ serialPort: "0",
+ },
+ showPartialVin: false,
+ deleteLogsOnExit: false,
+ };
+ }
+
+ // settings to save to localStorage
+ // save dtcs
+ // auto check for codes
+ // auto connect startup
+ // show partial vin
+ // delete logs on exit
+ const tId = event.target.id;
+ const checked = event.target.checked;
+
+ switch (tId) {
+ case 'save-dtcs':
+ settings.saveDtcs = checked;
+ break;
+ case 'auto-check-codes':
+ settings.autoCheckCodes = checked;
+ break;
+ case 'auto-connect':
+ // save connection config
+ settings.autoConnect = checked;
+ if (window.connectionConfig) {
+ settings.connectionConfig = window.connectionConfig;
+ }
+ break;
+ case 'hide-vin':
+ settings.showPartialVin = checked;
+ break;
+ case 'del-logs':
+ settings.deleteLogsOnExit = checked;
+ break;
+ // the rest will have the frontend send an event
+ // record response
+ // replay response
+ // use freeze frame
+ case 'record-responses':
+ // uncheck replay requests
+ document.getElementById('replay-responses').checked = false;
+ window.__TAURI__.event.emit('settings-changed', {tId: 'replay-responses', checked: false})
+ window.__TAURI__.event.emit('settings-changed', { tId, checked });
+ break;
+ case 'replay-responses':
+ // uncheck record requests
+ document.getElementById('record-responses').checked = false;
+ window.__TAURI__.event.emit('settings-changed', {tId: 'record-responses', checked: false})
+ window.__TAURI__.event.emit('settings-changed', { tId, checked });
+ break;
+ case 'use-freeze-frame':
+ window.__TAURI__.event.emit('settings-changed', { tId, checked });
+ break;
+ }
+
+ localStorage.setItem('userSettings', JSON.stringify(settings));
+ console.log("Saved settings:", settings);
+}
+
+window.addEventListener('DOMContentLoaded', async () => {
+ // sleep
+ await new Promise(r => setTimeout(r, 4000));
+
+ importSettings();
+
+ document.getElementById('record-responses').addEventListener('change', settingChange);
+ document.getElementById('replay-responses').addEventListener('change', settingChange);
+ document.getElementById('save-dtcs').addEventListener('change', settingChange);
+ document.getElementById('auto-check-codes').addEventListener('change', settingChange);
+ document.getElementById('use-freeze-frame').addEventListener('change', settingChange);
+ document.getElementById('auto-connect').addEventListener('change', settingChange);
+ document.getElementById('hide-vin').addEventListener('change', settingChange);
+ document.getElementById('del-logs').addEventListener('change', settingChange);
+});
diff --git a/frontend/scrollbar.css b/frontend/scrollbar.css
new file mode 100644
index 0000000..47d74b4
--- /dev/null
+++ b/frontend/scrollbar.css
@@ -0,0 +1,38 @@
+.grid::-webkit-scrollbar {
+ width: 13px;
+}
+
+.grid::-webkit-scrollbar-track {
+ background: #0F0F0F;
+ border-radius: 10px;
+}
+
+.grid::-webkit-scrollbar-thumb {
+ background-color: #29272c;
+ border-radius: 10px;
+ border: 2px solid #0F0F0F;
+}
+
+.grid::-webkit-scrollbar-thumb:hover {
+ background-color: #49454F;
+}
+
+
+.vin-results::-webkit-scrollbar {
+ width: 13px;
+}
+
+.vin-results::-webkit-scrollbar-track {
+ background: #0F0F0F;
+ border-radius: 10px;
+}
+
+.vin-results::-webkit-scrollbar-thumb {
+ background-color: #29272c;
+ border-radius: 10px;
+ border: 2px solid #0F0F0F;
+}
+
+.vin-results::-webkit-scrollbar-thumb:hover {
+ background-color: #49454F;
+}
\ No newline at end of file
diff --git a/frontend/styles.css b/frontend/styles.css
new file mode 100644
index 0000000..408782e
--- /dev/null
+++ b/frontend/styles.css
@@ -0,0 +1,92 @@
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font-family: 'Montserrat', sans-serif;
+ background-color: #121212;
+ color: #f0e9f9;
+ display: flex;
+ height: 100vh;
+ overflow: hidden;
+}
+
+h1 {
+ font-size: 1.8rem;
+ margin-bottom: 1.5rem;
+}
+
+
+/* Labels */
+
+.info {
+ display: flex;
+ flex-direction: column;
+}
+
+.connection {
+ font-size: 16px;
+ color: #F7F3FF;
+ font-weight: 500;
+}
+
+.car-model {
+ font-size: 1.5rem;
+ color: #F7F3FF;
+ font-weight: 700;
+}
+
+.connection {
+ font-size: 0.95rem;
+ color: #F7F3FF;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+}
+
+.connection-details {
+ font-size: 0.95rem;
+ color: #F7F3FF;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ margin-top: -12px;
+ margin-bottom: 12px;
+}
+
+.error-hint {
+ font-size: 0.7rem;
+ color: #575757;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ margin-top: 5px;
+ margin-bottom: 12px;
+}
+
+.label-row {
+ display: flex;
+ align-items: center;
+ gap: 5.7rem;
+}
+
+button.refresh {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 0;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.spinning {
+ animation: spin 0.3s linear 1;
+}
\ No newline at end of file
diff --git a/ref/dump.txt b/ref/dump.txt
deleted file mode 100644
index c97f9ec..0000000
--- a/ref/dump.txt
+++ /dev/null
@@ -1,460 +0,0 @@
-======================== SENDING MANUAL COMMANDS ========================
-
-> 0101
-< '7E8 06 41 01 03 07 E5 00 \r7EB 06 41 01 00 04 00 00 \r7EA 06 41 01 00 04 00 00 \r\r'
-
-> 0102
-< 'NO DATA\r\r'
-
-> 0103
-< '7E8 04 41 03 02 00 \r\r'
-
-> 0104
-< '7E8 03 41 04 3D \r\r'
-
-> 0105
-< '7E8 03 41 05 52 \r\r'
-
-> 0106
-< '7E8 03 41 06 81 \r\r'
-
-> 0107
-< '7E8 03 41 07 83 \r\r'
-
-> 0108
-< 'NO DATA\r\r'
-
-> 0109
-< 'NO DATA\r\r'
-
-> 010A
-< '7EB 03 41 0A 68 \r7E8 03 41 0A 67 \r\r'
-
-> 010B
-< '7E8 03 41 0B 20 \r\r'
-
-> 010C
-< '7E8 04 41 0C 0E 20 \r\r'
-
-> 010D
-< '7E8 03 41 0D 00 \r\r'
-
-> 010E
-< '7E8 03 41 0E 83 \r\r'
-
-> 010F
-< '7E8 03 41 0F 42 \r\r'
-
-> 0110
-< '7E8 04 41 10 01 22 \r\r'
-
-> 0111
-< '7E8 03 41 11 3B \r\r'
-
-> 0112
-< 'NO DATA\r\r'
-
-> 0113
-< '7E8 03 41 13 03 \r\r'
-
-> 0114
-< '7E8 04 41 14 90 7C \r\r'
-
-> 0115
-< '7E8 04 41 15 FF FF \r\r'
-
-> 0116
-< 'NO DATA\r\r'
-
-> 0117
-< 'NO DATA\r\r'
-
-> 0118
-< 'NO DATA\r\r'
-
-> 0119
-< 'NO DATA\r\r'
-
-> 01A0
-< 'NO DATA\r\r'
-
-> 01A1
-< 'NO DATA\r\r'
-
-> 0120
-< '7E8 06 41 20 80 07 E0 11 \r7EA 06 41 20 00 00 00 01 \r7EB 06 41 20 00 00 00 01 \r\r'
-
-> 0121
-< '7E8 04 41 21 00 0B \r\r'
-
-> 0122
-< 'CAN ERROR\r\r'
-
-> 0123
-< 'NO DATA\r\r'
-
-> 0122
-< 'NO DATA\r\r'
-
-> 0122
-< 'NO DATA\r\r'
-
-> 0124
-< 'NO DATA\r\r'
-
-> 0125
-< 'NO DATA\r\r'
-
-> 0126
-< 'NO DATA\r\r'
-
-> 0127
-< 'NO DATA\r\r'
-
-> EXIT
-< '?\r\r'
-
-> exit
-[ethan@archlinux obdium]$ cargo run
- Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.05s
- Running `target/debug/obdium`
-error when getting vin. vin length invalid. must be 17 characters
-
-======================== DIAGNOSTICS ========================
-ECU: 7E8
-ECU: 7EA
-ECU: 7EA
-ECU: 7E8
-ECU: 7E8
-ECU: 7EB
-ECU: 7E8
-Supported pids for ECUs
-ECU 7E8:
- 01 03 04 05 06 07 0A 0B 0C 0D
- 0E 0F 10 11 13 14 15 1C 1F 20
- 21 2E 2F 30 31 32 33 3C 40 41
- 42 43 44 45 46 47 49 4A 4C 4F
- 51 60 68
-ECU 7EB:
- 42
-ECU 7EA:
- 01 20 40
-Check engine light: false
-Number of trouble codes: 0
-OBD standard: No data
-Auxiliary input status: Inactive
-Control module voltage: NO DATA
-Distance traveled with malfunction indicator lamp: NO DATA
-TIme run with malfunction indicator lamp: NO DATA
-Time since codes cleared: NO DATA
-Warm-ups since codes cleared: NO DATA
-
-======================== ENGINE ========================
-Engine type: Unkown
-Engine speed: NO DATA
-Engine load: NO DATA
-Coolant temperature: NO DATA
-Coolant temperatue from sensors - Sensor 1: NO DATA - Sensor 2: NO DATA
-Engine fuel rate: NO DATA
-Engine runtime: NO DATA
-Engine runtime (diesel): NO DATA
-Engine mileage: NO DATA
-Engine oil temperature (Mode 1): NO DATA
-Engine oil temperature (Mode 22): 42°C
-Engine oil temperatue from sensors - Sensor 1: NO DATA - Sensor 2: NO DATA
-Engine oil pressure: 0kPa
-Drivers demand engine torque: NO DATA
-Actual engine torque: NO DATA
-Reference engine torque: NO DATA
-Engine percent torque data:
- Idle: NO DATA
- Engine point 1: NO DATA
- Engine point 2: NO DATA
- Engine point 3: NO DATA
- Engine point 4: NO DATA
-
-======================== FUEL SYSTEM ========================
-Short term fuel trim:
- Bank 1: NO DATA
- Bank 2: NO DATA
-Long term fuel trim:
- Bank 1: NO DATA
- Bank 2: NO DATA
-Fuel system status:
-Fuel system 1: Status("Unknown fuel system status")
-Fuel system 2: Status("Unknown fuel system status")
-Fuel pressure: NO DATA
-Fuel tank level: NO DATA
-Fuel rail pressure: NO DATA
-Fuel rail gauge pressure: NO DATA
-Fuel type: Type("Unknown")
-Ethanol fuel percentage: NO DATA
-Fuel injection timing: NO DATA
-Commanded EVAP purge: NO DATA
-EVAP system vapor pressure: NO DATA
-Cylinder fuel rate: NO DATA
-
-======================== SENSOR DATA ========================
-Vehicle speed: NO DATA
-Timing advance: NO DATA
-Throttle position: NO DATA
-Relative throttle position: NO DATA
-Absolute throttle position B: NO DATA
-Absolute throttle position C: NO DATA
-Accelerator pedal position D: NO DATA
-Accelerator pedal position E: NO DATA
-Accelerator pedal position F: NO DATA
-
-======================== AIR DATA ========================
-Oxygen Sensors:
-Sensor Voltage Short Term Trim (%) AFR / Voltage
-1 NO DATAv NO DATA NO DATA / NO DATA
-2 NO DATAv NO DATA NO DATA / NO DATA
-3 NO DATAv NO DATA NO DATA / NO DATA
-4 NO DATAv NO DATA NO DATA / NO DATA
-5 NO DATAv NO DATA NO DATA / NO DATA
-6 NO DATAv NO DATA NO DATA / NO DATA
-7 NO DATAv NO DATA NO DATA / NO DATA
-8 NO DATAv NO DATA NO DATA / NO DATA
-Mass air flow sensor:
- Sensor A: NO DATA
- Sensor B: NO DATA
-Maximum values for:
- Fuel-air equivalance ratio: NO DATA
- Oxygen sensor voltage: NO DATA
- Oxygen sensor current: NO DATA
- Intake manifold absolute pressure: NO DATA
-Intake air temperature: NO DATA
-Mass air-flow sensor rate: NO DATA
-Ambient air temperature: NO DATA
-Maximum air-flow rate from mass air-flow sensor: NO DATA
-Secondary air status: Status("Unknown secondary air status")
-Absolute barometric pressure: NO DATA
-
-======================== SENDING MANUAL COMMANDS ========================
-
-> exit
-[ethan@archlinux obdium]$ cargo clippy
- Checking obdium v0.1.0 (/home/ethan/obdium)
- Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.78s
-[ethan@archlinux obdium]$ cargo run
- Compiling obdium v0.1.0 (/home/ethan/obdium)
- Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.71s
- Running `target/debug/obdium`
-parsing iso_tp response: SEARCHING...\r7E8 10 14 49 02 01 4B 4C 34 \r7E8 21 43 4A 41 53 42 36 4A \r7E8 22 42 36 36 30 39 32 39 \r\r
-Bytes: ["7E8", "10", "14", "49", "02", "01", "4B", "4C", "34"]
-Bytes: ["7E8", "21", "43", "4A", "41", "53", "42", "36", "4A"]
-Bytes: ["7E8", "22", "42", "36", "36", "30", "39", "32", "39"]
-vin response: KL4!CJASB6J"B660929
-error when getting vin. vin length invalid. must be 17 characters
-
-======================== DIAGNOSTICS ========================
-ECU: 7EB
-ECU: 7EA
-ECU: 7E8
-ECU: 7EA
-ECU: 7EB
-ECU: 7E8
-ECU: 7E8
-Supported pids for ECUs
-ECU 7EA:
- 01 20 40
-ECU 7EB:
- 01 0A 20 42
-ECU 7E8:
- 21 2E 2F 30 31 32 33 3C 40 41
- 42 43 44 45 46 47 49 4A 4C 4F
- 51 60 68
-Check engine light: false
-Number of trouble codes: 3
-OBD standard: JOBD and OBD-II
-Auxiliary input status: Inactive
-Control module voltage: 0V
-Distance traveled with malfunction indicator lamp: 11km
-^C
-[ethan@archlinux obdium]$ cargo run
- Compiling obdium v0.1.0 (/home/ethan/obdium)
- Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.61s
- Running `target/debug/obdium`
-parsing iso_tp response: SEARCHING...\rUNABLE TO CONNECT\r\r
-Bytes: ["UNABLE", "TO", "CONNECT"]
-payload: []
-vin response:
-error when getting vin. vin length invalid. must be 17 characters
-
-======================== DIAGNOSTICS ========================
-^C
-[ethan@archlinux obdium]$ cargo run
- Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s
- Running `target/debug/obdium`
-parsing iso_tp response: SEARCHING...\rUNABLE TO CONNECT\r\r
-Bytes: ["UNABLE", "TO", "CONNECT"]
-payload: []
-vin response:
-error when getting vin. vin length invalid. must be 17 characters
-
-======================== DIAGNOSTICS ========================
-^C
-[ethan@archlinux obdium]$ cargo run
- Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s
- Running `target/debug/obdium`
-parsing iso_tp response: SEARCHING...\r7E8 10 14 49 02 01 4B 4C 34 \r7E8 21 43 4A 41 53 42 36 4A \r7E8 22 42 36 36 30 39 32 39 \r\r
-Bytes: ["7E8", "10", "14", "49", "02", "01", "4B", "4C", "34"]
-Bytes: ["7E8", "21", "43", "4A", "41", "53", "42", "36", "4A"]
-Bytes: ["7E8", "22", "42", "36", "36", "30", "39", "32", "39"]
-payload: ["01", "4B", "4C", "34", "21", "43", "4A", "41", "53", "42", "36", "4A", "22", "42", "36", "36", "30", "39", "32", "39"]
-vin response: KL4!CJASB6J"B660929
-error when getting vin. vin length invalid. must be 17 characters
-
-======================== DIAGNOSTICS ========================
-ECU: 7E8
-ECU: 7EB
-ECU: 7E8
-ECU: 7EB
-ECU: 7E8
-ECU: 7EB
-ECU: 7E8
-Supported pids for ECUs
-ECU 7EB:
- 01 0A 20 40 42
-ECU 7E8:
- 01 03 04 05 06 07 0A 0B 0C 0D
- 0E 0F 10 11 13 14 15 1C 1F 20
- 21 2E 2F 30 31 32 33 3C 40 41
- 42 43 44 45 46 47 49 4A 4C 4F
- 51 60 68
-Check engine light: false
-Number of trouble codes: 3
-OBD standard: JOBD and OBD-II
-Auxiliary input status: Inactive
-Control module voltage: 14.705V
-Distance traveled with malfunction indicator lamp: 11km
-TIme run with malfunction indicator lamp: NO DATA
-Time since codes cleared: NO DATA
-Warm-ups since codes cleared: NO DATA
-
-======================== ENGINE ========================
-Engine type: Internal Combustion Engine
-Engine speed: 1458RPM
-Engine load: 27.450981%
-^C
-[ethan@archlinux obdium]$ cargo run
- Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s
- Running `target/debug/obdium`
-parsing iso_tp response: SEARCHING...\r7E8 10 14 49 02 01 4B 4C 34 \r7E8 21 43 4A 41 53 42 36 4A \r7E8 22 42 36 36 30 39 32 39 \r\r
-Bytes: ["7E8", "10", "14", "49", "02", "01", "4B", "4C", "34"]
-Bytes: ["7E8", "21", "43", "4A", "41", "53", "42", "36", "4A"]
-Bytes: ["7E8", "22", "42", "36", "36", "30", "39", "32", "39"]
-payload: ["01", "4B", "4C", "34", "21", "43", "4A", "41", "53", "42", "36", "4A", "22", "42", "36", "36", "30", "39", "32", "39"]
-vin response: KL4!CJASB6J"B660929
-error when getting vin. vin length invalid. must be 17 characters
-
-======================== DIAGNOSTICS ========================
-ECU: 7EA
-ECU: 7EB
-ECU: 7EA
-ECU: 7EB
-ECU: 7EA
-ECU: 7E8
-ECU: 7E8
-Supported pids for ECUs
-ECU 7EB:
- 01 0A 20 40
-ECU 7EA:
- 01 20 40 42
-ECU 7E8:
- 41 42 43 44 45 46 47 49 4A 4C
- 4F 51 60 68
-Check engine light: false
-Number of trouble codes: 0
-OBD standard: JOBD and OBD-II
-Auxiliary input status: Inactive
-Control module voltage: 14.646V
-Distance traveled with malfunction indicator lamp: 11km
-TIme run with malfunction indicator lamp: NO DATA
-Time since codes cleared: NO DATA
-Warm-ups since codes cleared: NO DATA
-
-======================== ENGINE ========================
-Engine type: Internal Combustion Engine
-Engine speed: 741.75RPM
-Engine load: 21.176472%
-Coolant temperature: 53°C
-Coolant temperatue from sensors - Sensor 1: NO DATA - Sensor 2: NO DATA
-Engine fuel rate: NO DATA
-Engine runtime: 1792s
-Engine runtime (diesel): NO DATA
-Engine mileage: NO DATA
-Engine oil temperature (Mode 1): NO DATA
-Engine oil temperature (Mode 22): 43°C
-Engine oil temperatue from sensors - Sensor 1: NO DATA - Sensor 2: NO DATA
-Engine oil pressure: 0kPa
-Drivers demand engine torque: NO DATA
-Actual engine torque: NO DATA
-Reference engine torque: NO DATA
-Engine percent torque data:
- Idle: NO DATA
- Engine point 1: NO DATA
- Engine point 2: NO DATA
- Engine point 3: NO DATA
- Engine point 4: NO DATA
-
-======================== FUEL SYSTEM ========================
-Short term fuel trim:
- Bank 1: 3.90625%
- Bank 2: NO DATA
-Long term fuel trim:
- Bank 1: 0.78125%
- Bank 2: NO DATA
-Fuel system status:
-Fuel system 1: Status("Closed loop, using O2 sensor feedback for fuel mix")
-Fuel system 2: Status("Motor is off")
-Fuel pressure: 294kPa
-Fuel tank level: 26.27451%
-Fuel rail pressure: NO DATA
-Fuel rail gauge pressure: NO DATA
-Fuel type: Type("Gasoline")
-Ethanol fuel percentage: NO DATA
-Fuel injection timing: NO DATA
-Commanded EVAP purge: 0%
-EVAP system vapor pressure: 16383Pa
-Cylinder fuel rate: NO DATA
-
-======================== SENSOR DATA ========================
-Vehicle speed: 0Kmh
-Timing advance: 2°
-Throttle position: 21.176472%
-Relative throttle position: 7.4509807%
-Absolute throttle position B: 20.784313%
-Absolute throttle position C: NO DATA
-Accelerator pedal position D: 19.607843%
-Accelerator pedal position E: 9.803922%
-Accelerator pedal position F: NO DATA
-
-======================== AIR DATA ========================
-Oxygen Sensors:
-Sensor Voltage Short Term Trim (%) AFR / Voltage
-1 0.78Vv -1.5625% NO DATA / NO DATA
-2 1.275Vv 99.21875% NO DATA / NO DATA
-3 NO DATAv NO DATA NO DATA / NO DATA
-4 NO DATAv NO DATA NO DATA / NO DATA
-5 NO DATAv NO DATA NO DATA / NO DATA
-6 NO DATAv NO DATA NO DATA / NO DATA
-7 NO DATAv NO DATA NO DATA / NO DATA
-8 NO DATAv NO DATA NO DATA / NO DATA
-Mass air flow sensor:
- Sensor A: NO DATA
- Sensor B: NO DATA
-Maximum values for:
- Fuel-air equivalance ratio: 0ratio
- Oxygen sensor voltage: 0V
- Oxygen sensor current: 0mA
- Intake manifold absolute pressure: 310kPa
-Intake air temperature: 29°C
-Mass air-flow sensor rate: 2.33g/s
-Ambient air temperature: 26°C
-Maximum air-flow rate from mass air-flow sensor: NO DATA
-Secondary air status: Status("Unknown secondary air status")
-Absolute barometric pressure: 98kPa
-
-======================== SENDING MANUAL COMMANDS ========================
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 948bbdb..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,281 +0,0 @@
-use obdium::{
- obd::{
- BankNumber,
- Error,
- SensorNumber,
- Service,
- OBD
- },
- diagnostics::Test
-};
-
-use std::fmt::Write;
-
-fn print_tests_table(title: &str, tests: &[Test]) {
- let mut table = String::new();
- writeln!(table, "{title}").unwrap();
- writeln!(
- table,
- "{:<35} | {:<10} | {:<10}",
- "Test Name", "Available", "Complete"
- )
- .unwrap();
- writeln!(table, "{:-<35}-+-{:-<10}-+-{:-<10}", "", "", "").unwrap();
-
- for test in tests {
- writeln!(
- table,
- "{:<35} | {:<10} | {:<10}",
- test.name,
- if test.available { "Yes" } else { "No" },
- if test.complete { "Yes" } else { "No" },
- )
- .unwrap();
- }
-
- println!("{table}");
-}
-
-fn main() -> Result<(), Error> {
- let mut obd = OBD::new();
- //obd.replay_requests(true);
- //obd.record_requests(true);
- obd.connect("COM4", 38400)?;
-
- println!("\n{} DIAGNOSTICS {}", "=".repeat(24), "=".repeat(24));
- let supported_pids = obd.get_service_supported_pids("01");
-
- println!("Supported pids for ECUs");
- for (ecu_name, pids) in supported_pids.iter() {
- print!("ECU {ecu_name}:");
- for (index, pid) in pids.iter().enumerate() {
- if index % 10 == 0 {
- print!("\n\t");
- }
-
- print!("{pid} ");
- if index == pids.len() - 1 {
- println!()
- }
- }
- }
-
- println!("Check engine light: {}", obd.check_engine_light());
- println!("Number of trouble codes: {}", obd.get_num_trouble_codes());
-
- let codes = obd.get_trouble_codes();
- for code in codes {
- println!("{}\n", code);
- }
-
- println!("OBD standard: {}", obd.obd_standards());
- println!("Auxiliary input status: {}", obd.aux_input_status());
- println!("Control module voltage: {}", obd.control_module_voltage());
- println!(
- "Distance traveled with malfunction indicator lamp: {}",
- obd.distance_traveled_with_mil()
- );
- println!(
- "TIme run with malfunction indicator lamp: {}",
- obd.time_run_with_mil()
- );
- println!(
- "Time since codes cleared: {}",
- obd.time_since_codes_cleared()
- );
- println!(
- "Warm-ups since codes cleared: {}",
- obd.warm_ups_since_codes_cleared()
- );
- println!("Protocol: {}", obd.get_protocol().unwrap());
-
- println!();
- print_tests_table("Common tests", &obd.get_common_tests_status());
- print_tests_table("Advanced tests", &obd.get_advanced_tests_status());
-
- println!("{} ENGINE {}", "=".repeat(24), "=".repeat(24));
- println!("Engine type: {}", obd.get_engine_type());
- println!("Engine speed: {}", obd.rpm());
- println!("Engine load: {}", obd.engine_load());
-
- let coolant_temp = obd.coolant_temp_sensors();
- println!("Coolant temperature: {}", obd.coolant_temp());
- println!(
- "Coolant temperatue from sensors - Sensor 1: {} - Sensor 2: {} ",
- coolant_temp.0, coolant_temp.1
- );
- println!("Engine fuel rate: {}", obd.engine_fuel_rate());
- println!("Engine runtime: {}", obd.engine_runtime());
- println!("Engine runtime (diesel): {}", obd.engine_runtime_diesel());
- println!("Engine mileage: {}", obd.odometer());
-
- let oil_temp = obd.engine_oil_temp_sensors();
- println!(
- "Engine oil temperature (Mode 1): {}",
- obd.engine_oil_temp(Service::Mode01)
- );
- println!(
- "Engine oil temperature (Mode 22): {}",
- obd.engine_oil_temp(Service::Mode22)
- );
- println!(
- "Engine oil temperatue from sensors - Sensor 1: {} - Sensor 2: {} ",
- oil_temp.0, oil_temp.1
- );
- println!("Engine oil pressure: {}", obd.engine_oil_pressure()); // Mode 22 PID
-
- println!(
- "Drivers demand engine torque: {}",
- obd.drivers_demand_engine_torque()
- );
- println!("Actual engine torque: {}", obd.actual_engine_torque());
- println!("Reference engine torque: {}", obd.reference_engine_torque());
-
- let percent_torque_data = obd.engine_percent_torque_data();
- println!("Engine percent torque data:");
- println!("\tIdle: {}", percent_torque_data.0);
- println!("\tEngine point 1: {}", percent_torque_data.1);
- println!("\tEngine point 2: {}", percent_torque_data.2);
- println!("\tEngine point 3: {}", percent_torque_data.3);
- println!("\tEngine point 4: {}", percent_torque_data.4);
-
- println!("\n{} FUEL SYSTEM {}", "=".repeat(24), "=".repeat(24));
-
- println!("Short term fuel trim:");
- println!("\tBank 1: {}", obd.short_term_fuel_trim(BankNumber::Bank1));
- println!("\tBank 2: {}", obd.short_term_fuel_trim(BankNumber::Bank2));
-
- println!("Long term fuel trim:");
- println!("\tBank 1: {}", obd.long_term_fuel_trim(BankNumber::Bank1));
- println!("\tBank 2: {}", obd.long_term_fuel_trim(BankNumber::Bank2));
-
- let fuel_system_status = obd.fuel_system_status();
- println!("Fuel system status:");
- println!("Fuel system 1: {:?}", fuel_system_status.0);
- println!("Fuel system 2: {:?}", fuel_system_status.1);
-
- println!("Fuel pressure: {}", obd.fuel_pressure());
- println!("Fuel tank level: {}", obd.fuel_tank_level());
- println!("Fuel rail pressure: {}", obd.fuel_rail_pressure());
- println!(
- "Fuel rail gauge pressure: {}",
- obd.fuel_rail_guage_pressure()
- );
- println!("Fuel type: {:?}", obd.fuel_type());
- println!("Ethanol fuel percentage: {}", obd.ethanol_fuel_percentage());
- println!("Fuel injection timing: {}", obd.fuel_injection_timing());
- println!("Commanded EVAP purge: {}", obd.commanded_evap_purge());
- println!(
- "EVAP system vapor pressure: {}",
- obd.evap_system_vapor_pressure()
- );
- println!("Cylinder fuel rate: {}", obd.cylinder_fuel_rate());
-
- println!("\n{} SENSOR DATA {}", "=".repeat(24), "=".repeat(24));
- println!("Vehicle speed: {}", obd.vehicle_speed());
- println!("Timing advance: {}", obd.timing_advance());
-
- println!("Throttle position: {}", obd.throttle_position());
- println!(
- "Relative throttle position: {}",
- obd.relative_throttle_pos()
- );
- println!(
- "Absolute throttle position B: {}",
- obd.abs_throttle_position_b()
- );
- println!(
- "Absolute throttle position C: {}",
- obd.abs_throttle_position_c()
- );
- println!(
- "Accelerator pedal position D: {}",
- obd.acc_pedal_position_d()
- );
- println!(
- "Accelerator pedal position E: {}",
- obd.acc_pedal_position_e()
- );
- println!(
- "Accelerator pedal position F: {}",
- obd.acc_pedal_position_f()
- );
-
- // Read oxcygen sensors 1-8
- let sensors = [
- SensorNumber::Sensor1,
- SensorNumber::Sensor2,
- SensorNumber::Sensor3,
- SensorNumber::Sensor4,
- SensorNumber::Sensor5,
- SensorNumber::Sensor6,
- SensorNumber::Sensor7,
- SensorNumber::Sensor8,
- ];
-
- let mut data = Vec::new();
-
- for (i, sensor) in sensors.iter().enumerate() {
- let (voltage1, trim) = obd.read_oxygen_sensor(sensor);
- let (afr, voltage2) = obd.o2_sensor_air_fuel_ratio(sensor);
- data.push((i + 1, voltage1, trim, afr, voltage2));
- }
-
- println!("\n{} AIR DATA {}", "=".repeat(24), "=".repeat(24));
- println!("Oxygen Sensors:");
- println!(
- "{:<8} | {:<10} | {:<22} | {:<22}",
- "Sensor", "Voltage", "Short Term Trim (%)", "AFR / Voltage"
- );
- println!("{:-<8}-+-{:-<10}-+-{:-<22}-+-{:-<22}", "", "", "", "");
-
- for (sensor_id, v1, trim, afr, v2) in data {
- println!(
- "{:<8} | {:<10} | {:<22} | {:<22}",
- sensor_id,
- format!("{v1}"),
- format!("{trim}"),
- format!("{afr} / {v2}")
- );
- }
-
- let maf = obd.read_mass_air_flow_sensor();
- println!("Mass air flow sensor:");
- println!("\tSensor A: {}", maf.0);
- println!("\tSensor B: {}", maf.1);
-
- let max_values_for = obd.max_values_for();
- println!("Maximum values for:");
- println!("\tFuel-air equivalance ratio: {}", max_values_for.0);
- println!("\tOxygen sensor voltage: {}", max_values_for.1);
- println!("\tOxygen sensor current: {}", max_values_for.2);
- println!("\tIntake manifold absolute pressure: {}", max_values_for.3);
-
- println!("Intake air temperature: {}", obd.intake_air_temp());
- println!("Mass air-flow sensor rate: {}", obd.maf_air_flow_rate());
- println!("Ambient air temperature: {}", obd.ambient_air_temp());
- println!(
- "Maximum air-flow rate from mass air-flow sensor: {}",
- obd.max_air_flow_rate_from_maf()
- );
-
- println!("Secondary air status: {:?}", obd.secondary_air_status());
- println!(
- "Absolute barometric pressure: {}",
- obd.abs_barometric_pressure()
- );
-
- println!(
- "\n{} SENDING MANUAL COMMANDS {}",
- "=".repeat(24),
- "=".repeat(24)
- );
-
- if let Some(vin) = obd.get_vin() {
- obd.test_mode_22_pids(&vin);
- }
-
- obd.read_from_user_input();
-
- Ok(())
-}