Add packet.

This commit is contained in:
Michael Mikovsky
2026-05-16 14:14:00 -06:00
parent 56abb5e1e0
commit 129720145a
8 changed files with 470 additions and 773 deletions
Generated
+3 -670
View File
@@ -22,12 +22,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android_system_properties"
version = "0.1.5"
@@ -124,15 +118,6 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "castaway"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
dependencies = [
"rustversion",
]
[[package]]
name = "cbc"
version = "0.2.0"
@@ -204,35 +189,12 @@ dependencies = [
"inout",
]
[[package]]
name = "compact_str"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a"
dependencies = [
"castaway",
"cfg-if",
"itoa",
"rustversion",
"ryu",
"static_assertions",
]
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "convert_case"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
@@ -254,48 +216,6 @@ dependencies = [
"libc",
]
[[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-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crossterm"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
dependencies = [
"bitflags 2.11.1",
"crossterm_winapi",
"derive_more",
"document-features",
"mio",
"parking_lot",
"rustix",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
dependencies = [
"winapi",
]
[[package]]
name = "crypto-common"
version = "0.2.1"
@@ -305,71 +225,6 @@ dependencies = [
"hybrid-array",
]
[[package]]
name = "darling"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0"
dependencies = [
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.117",
]
[[package]]
name = "darling_macro"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
dependencies = [
"darling_core",
"quote",
"syn 2.0.117",
]
[[package]]
name = "deranged"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
dependencies = [
"powerfmt",
]
[[package]]
name = "derive_more"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn 2.0.117",
]
[[package]]
name = "digest"
version = "0.11.2"
@@ -381,54 +236,12 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "document-features"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
dependencies = [
"litrs",
]
[[package]]
name = "downcast-rs"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[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.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "filedescriptor"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d"
dependencies = [
"libc",
"thiserror 1.0.69",
"winapi",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.8"
@@ -441,12 +254,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "getrandom"
version = "0.4.2"
@@ -467,7 +274,7 @@ version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
"foldhash",
]
[[package]]
@@ -475,11 +282,6 @@ name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash 0.2.0",
]
[[package]]
name = "hashbrown"
@@ -544,12 +346,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "indexmap"
version = "2.13.0"
@@ -562,15 +358,6 @@ dependencies = [
"serde_core",
]
[[package]]
name = "indoc"
version = "2.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
dependencies = [
"rustversion",
]
[[package]]
name = "inout"
version = "0.2.2"
@@ -581,28 +368,6 @@ dependencies = [
"hybrid-array",
]
[[package]]
name = "instability"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971"
dependencies = [
"darling",
"indoc",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.18"
@@ -619,23 +384,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kasuari"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899"
dependencies = [
"hashbrown 0.16.1",
"portable-atomic",
"thiserror 2.0.18",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "leb128fmt"
version = "0.1.0"
@@ -648,27 +396,6 @@ version = "0.2.185"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
[[package]]
name = "line-clipping"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8"
dependencies = [
"bitflags 2.11.1",
]
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "litrs"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
[[package]]
name = "lock_api"
version = "0.4.14"
@@ -684,33 +411,12 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lru"
version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39"
dependencies = [
"hashbrown 0.16.1",
]
[[package]]
name = "memchr"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "mio"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
]
[[package]]
name = "munge"
version = "0.4.7"
@@ -731,24 +437,6 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "nix"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.11.1",
"cfg-if",
"cfg_aliases 0.1.1",
"libc",
]
[[package]]
name = "num-conv"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -758,15 +446,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]]
name = "once_cell"
version = "1.21.3"
@@ -796,39 +475,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "portable-pty"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4a596a2b3d2752d94f51fac2d4a96737b8705dddd311a32b9af47211f08671e"
dependencies = [
"anyhow",
"bitflags 1.3.2",
"downcast-rs",
"filedescriptor",
"lazy_static",
"libc",
"log",
"nix",
"serial2",
"shared_library",
"shell-words",
"winapi",
"winreg",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "prettyplease"
version = "0.2.37"
@@ -909,69 +555,6 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "ratatui"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc"
dependencies = [
"instability",
"ratatui-core",
"ratatui-crossterm",
"ratatui-widgets",
]
[[package]]
name = "ratatui-core"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293"
dependencies = [
"bitflags 2.11.1",
"compact_str",
"hashbrown 0.16.1",
"indoc",
"itertools",
"kasuari",
"lru",
"strum",
"thiserror 2.0.18",
"unicode-segmentation",
"unicode-truncate",
"unicode-width",
]
[[package]]
name = "ratatui-crossterm"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3"
dependencies = [
"cfg-if",
"crossterm",
"instability",
"ratatui-core",
]
[[package]]
name = "ratatui-widgets"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db"
dependencies = [
"bitflags 2.11.1",
"hashbrown 0.16.1",
"indoc",
"instability",
"itertools",
"line-clipping",
"ratatui-core",
"strum",
"time",
"unicode-segmentation",
"unicode-width",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
@@ -1049,40 +632,12 @@ dependencies = [
"syn 2.0.117",
]
[[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.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags 2.11.1",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -1137,17 +692,6 @@ dependencies = [
"zmij",
]
[[package]]
name = "serial2"
version = "0.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcdbc46aa3882ec3d48ec2b5abcb4f0d863a13d7599265f3faa6d851f23c12f3"
dependencies = [
"cfg-if",
"libc",
"winapi",
]
[[package]]
name = "sha2"
version = "0.11.0"
@@ -1159,59 +703,12 @@ dependencies = [
"digest",
]
[[package]]
name = "shared_library"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
dependencies = [
"lazy_static",
"libc",
]
[[package]]
name = "shell-words"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]]
name = "simdutf8"
version = "0.1.5"
@@ -1224,12 +721,6 @@ version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "static_init"
version = "1.0.4"
@@ -1258,33 +749,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "syn"
version = "1.0.109"
@@ -1307,33 +771,13 @@ dependencies = [
"unicode-ident",
]
[[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.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl 2.0.18",
]
[[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.117",
"thiserror-impl",
]
[[package]]
@@ -1347,27 +791,6 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "time"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde_core",
"time-core",
]
[[package]]
name = "time-core"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "tinyvec"
version = "1.11.0"
@@ -1383,17 +806,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "treetest"
version = "0.1.0"
dependencies = [
"crossbeam-channel",
"crossterm",
"ratatui",
"thiserror 2.0.18",
"unshell",
]
[[package]]
name = "typenum"
version = "1.20.0"
@@ -1406,29 +818,6 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-segmentation"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
[[package]]
name = "unicode-truncate"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5"
dependencies = [
"itertools",
"unicode-segmentation",
"unicode-width",
]
[[package]]
name = "unicode-width"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]]
name = "unicode-xid"
version = "0.2.6"
@@ -1440,34 +829,10 @@ name = "unshell"
version = "0.1.0"
dependencies = [
"chrono",
"crossbeam-channel",
"rkyv",
"static_init",
"thiserror 2.0.18",
"unshell-leaves",
"unshell-macros",
"thiserror",
"unshell-protocol",
"unshell-runtime",
]
[[package]]
name = "unshell-leaves"
version = "0.1.0"
dependencies = [
"crossbeam-channel",
"portable-pty",
"rkyv",
"unshell-macros",
"unshell-protocol",
]
[[package]]
name = "unshell-macros"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
@@ -1475,14 +840,6 @@ name = "unshell-protocol"
version = "0.1.0"
dependencies = [
"rkyv",
"unshell-macros",
]
[[package]]
name = "unshell-runtime"
version = "0.1.0"
dependencies = [
"unshell-protocol",
]
[[package]]
@@ -1511,12 +868,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.3+wasi-0.2.9"
@@ -1695,24 +1046,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
+13 -67
View File
@@ -4,11 +4,11 @@ cargo-features = ["trim-paths", "panic-immediate-abort"]
members = [
"ush-obfuscate",
"base62",
"unshell-macros",
# "unshell-macros",
"unshell-protocol",
"unshell-runtime",
"unshell-leaves",
"treetest",
# "unshell-runtime",
# "unshell-leaves",
# "treetest",
]
resolver = "2"
@@ -32,9 +32,9 @@ portable-pty = "0.9.0"
crossbeam-channel = "0.5.15"
unshell = { path = "." }
unshell-protocol = { path = "./unshell-protocol" }
unshell-runtime = { path = "./unshell-runtime" }
unshell-leaves = { path = "./unshell-leaves" }
unshell-macros = { path = "./unshell-macros" }
# unshell-runtime = { path = "./unshell-runtime" }
# unshell-leaves = { path = "./unshell-leaves" }
# unshell-macros = { path = "./unshell-macros" }
# ush-obfuscate = { path = "./ush-obfuscate" }
# base62 = { path = "./base62" }
@@ -51,8 +51,8 @@ log = []
log_debug = ["log", "dep:chrono"]
# Leaf features
leaf_endpoint = ["unshell-leaves/leaf_endpoint"]
leaf_tui = ["unshell-leaves/leaf_tui"]
# leaf_endpoint = ["unshell-leaves/leaf_endpoint"]
# leaf_tui = ["unshell-leaves/leaf_tui"]
# obfuscate_aes = ["ush-obfuscate/obfuscate_aes"]
# obfuscate_ref = ["ush-obfuscate/obfuscate_ref"]
@@ -63,64 +63,10 @@ thiserror = { workspace = true, optional = true }
chrono = { workspace = true, optional = true }
# ush-obfuscate = { workspace = true }
static_init = { workspace = true }
unshell-macros = { workspace = true }
# unshell-macros = { workspace = true }
unshell-protocol = { workspace = true }
unshell-runtime = { workspace = true }
unshell-leaves = { workspace = true }
[dev-dependencies]
crossbeam-channel = { workspace = true }
[[example]]
name = "leaf_derive"
path = "examples/protocol/leaf_derive.rs"
[[example]]
name = "crossbeam_channel_leaf"
path = "examples/protocol/crossbeam_channel_leaf.rs"
[[example]]
name = "runtime_leaf_actions"
path = "examples/protocol/runtime_leaf_actions.rs"
[[example]]
name = "remote_shell_endpoint"
path = "examples/protocol/remote_shell_endpoint.rs"
required-features = ["leaf_endpoint"]
[[example]]
name = "remote_shell_receive"
path = "examples/protocol/remote_shell_receive.rs"
required-features = ["leaf_endpoint"]
[[example]]
name = "remote_shell_single_endpoint"
path = "examples/protocol/remote_shell_single_endpoint.rs"
required-features = ["leaf_endpoint"]
[[example]]
name = "bench"
path = "examples/protocol/bench/bench.rs"
[[example]]
name = "op_encode_call"
path = "examples/protocol/bench/op_encode_call.rs"
[[example]]
name = "op_decode_call"
path = "examples/protocol/bench/op_decode_call.rs"
[[example]]
name = "op_forward_call_receive"
path = "examples/protocol/bench/op_forward_call_receive.rs"
[[example]]
name = "op_local_call_receive"
path = "examples/protocol/bench/op_local_call_receive.rs"
[[example]]
name = "op_hook_data_receive"
path = "examples/protocol/bench/op_hook_data_receive.rs"
# unshell-runtime = { workspace = true }
# unshell-leaves = { workspace = true }
[profile.minimize]
inherits = "release"
@@ -143,4 +89,4 @@ unsafe_op_in_unsafe_fn = "warn"
unused_import_braces = "warn"
unused_lifetimes = "warn"
trivial_casts = "allow"
missing_docs = "warn"
# missing_docs = "warn"
+18 -18
View File
@@ -24,12 +24,12 @@ pub mod logger;
pub use unshell_protocol as protocol;
/// Re-export the leaf library crate behind the historical `unshell::leaves` path
pub use unshell_leaves as leaves;
// pub use unshell_leaves as leaves;
/// Re-export the runtime crate behind the `unshell::runtime` path.
pub use unshell_runtime as runtime;
// pub use unshell_runtime as runtime;
pub use unshell_macros::{Procedure, leaf, procedures};
// pub use unshell_macros::{Procedure, leaf, procedures};
/// Creates a root-assumed endpoint from one local identifier plus any number of leaf hosts.
///
@@ -40,21 +40,21 @@ pub use unshell_macros::{Procedure, leaf, procedures};
/// Why it exists: the common bootstrap case should not require callers to manually construct an
/// empty path, `Vec<ChildRoute>`, and a `Vec<LeafSpec>` when they already have leaf host values.
///
/// # Example
/// ```rust
/// use unshell::{create_endpoint, leaf};
/// use unshell::protocol::tree::Endpoint;
///
/// #[derive(Default)]
/// struct DemoLeaf;
///
/// #[leaf(id = "org.example.v1.demo", procedures = ["ping"], endpoint_struct = DemoLeaf)]
/// struct Demo;
///
/// let endpoint = create_endpoint!("demo", DemoLeaf::default());
/// assert!(endpoint.path().is_empty());
/// assert_eq!(endpoint.local_id(), Some("demo"));
/// ```
// # Example
// ```rust
// use unshell::{create_endpoint, leaf};
// use unshell::protocol::tree::Endpoint;
// #[derive(Default)]
// struct DemoLeaf;
// #[leaf(id = "org.example.v1.demo", procedures = ["ping"], endpoint_struct = DemoLeaf)]
// struct Demo;
// let endpoint = create_endpoint!("demo", DemoLeaf::default());
// assert!(endpoint.path().is_empty());
// assert_eq!(endpoint.local_id(), Some("demo"));
// ```
#[macro_export]
macro_rules! create_endpoint {
($id:expr $(, $leaf:expr )* $(,)?) => {{
+2 -2
View File
@@ -9,7 +9,7 @@ doctest = false
[dependencies]
rkyv = { workspace = true }
unshell-macros = { path = "../unshell-macros" }
# unshell-macros = { path = "../unshell-macros" }
[lints.rust]
elided_lifetimes_in_paths = "warn"
@@ -22,4 +22,4 @@ unsafe_op_in_unsafe_fn = "warn"
unused_import_braces = "warn"
unused_lifetimes = "warn"
trivial_casts = "allow"
missing_docs = "warn"
# missing_docs = "warn"
+2 -16
View File
@@ -1,20 +1,6 @@
//! # UnShell Protocol
//!
//! The protocol crate owns the wire types, framing, validation helpers, and the
//! small tree runtime used by endpoint implementations.
#![no_std]
pub extern crate alloc;
#[allow(unused_extern_crates)]
extern crate self as unshell;
/// Keep the historical nested path so existing imports and proc-macro output can
/// continue to target `unshell::protocol::...` while the implementation lives in
/// its own crate.
pub mod protocol;
pub use protocol::*;
#[cfg(test)]
pub use unshell_macros::{Procedure, leaf, procedures};
pub mod packet;
pub mod utils;
+167
View File
@@ -0,0 +1,167 @@
#[cfg(test)]
mod tests;
extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
#[derive(Debug)]
pub struct Packet {
pub hook_id: u16,
pub is_upwards_call: bool,
pub end_hook: bool,
pub path: String,
// ── body (routers never read below this line) ──
pub procedure_id: String,
pub data: Vec<u8>,
}
/// Returned by `deserialize_header` — only what a router needs.
/// `body_remainder` is a raw slice into the original buffer so the
/// entire body can be forwarded without touching it.
#[derive(Debug)]
pub struct HeaderRef<'buf> {
pub hook_id: u16,
pub is_upwards_call: bool,
pub end_hook: bool,
pub path: &'buf str,
pub body_remainder: &'buf [u8],
}
#[derive(Debug)]
pub enum SerializeError {
PathTooLarge,
ProcIdTooLarge,
BodyTooLarge,
}
#[derive(Debug, PartialEq)]
pub enum DeserializeError {
BufferTooShort,
BodyLengthMismatch,
PathTooLong,
ProcIdTooLong,
InvalidUtf8,
}
impl Packet {
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
let path_bytes = self.path.as_bytes();
let proc_id_bytes = self.procedure_id.as_bytes();
let path_len = u32::try_from(path_bytes.len()).map_err(|_| SerializeError::PathTooLarge)?;
let proc_id_len =
u32::try_from(proc_id_bytes.len()).map_err(|_| SerializeError::ProcIdTooLarge)?;
// body = proc_id_len field + proc_id bytes + data bytes
let body_payload_len = 4usize
.checked_add(proc_id_bytes.len())
.and_then(|n| n.checked_add(self.data.len()))
.ok_or(SerializeError::BodyTooLarge)?;
let body_len = u32::try_from(body_payload_len).map_err(|_| SerializeError::BodyTooLarge)?;
let total = 8 + path_bytes.len() + 4 + body_payload_len;
let mut buf = Vec::with_capacity(total);
// ── header ────────────────────────────────────────────────────────────
let flags = (self.is_upwards_call as u8) | ((self.end_hook as u8) << 1);
buf.extend_from_slice(&self.hook_id.to_le_bytes());
buf.push(flags);
buf.push(0u8); // padding
buf.extend_from_slice(&path_len.to_le_bytes());
buf.extend_from_slice(path_bytes);
// ── body ──────────────────────────────────────────────────────────────
buf.extend_from_slice(&body_len.to_le_bytes());
buf.extend_from_slice(&proc_id_len.to_le_bytes());
buf.extend_from_slice(proc_id_bytes);
buf.extend_from_slice(&self.data);
Ok(buf)
}
/// Deserialize only the header — O(path_len), body bytes are never read.
/// A router can inspect `HeaderRef` then forward the original buffer as-is.
pub fn deserialize_header(buf: &[u8]) -> Result<HeaderRef<'_>, DeserializeError> {
// fixed prefix: hook_id (2) + flags (1) + padding (1) + path_len (4)
if buf.len() < 8 {
return Err(DeserializeError::BufferTooShort);
}
let hook_id = u16::from_le_bytes([buf[0], buf[1]]);
let flags = buf[2];
let is_upwards_call = flags & 0b0000_0001 != 0;
let end_hook = flags & 0b0000_0010 != 0;
let path_len = u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]) as usize;
let path_start = 8usize;
let path_end = path_start
.checked_add(path_len)
.ok_or(DeserializeError::PathTooLong)?;
if buf.len() < path_end {
return Err(DeserializeError::BufferTooShort);
}
let path = core::str::from_utf8(&buf[path_start..path_end])
.map_err(|_| DeserializeError::InvalidUtf8)?;
Ok(HeaderRef {
hook_id,
is_upwards_call,
end_hook,
path,
body_remainder: &buf[path_end..],
})
}
/// Full deserialization. Parses the header then the body.
pub fn deserialize(buf: &[u8]) -> Result<Self, DeserializeError> {
let header = Self::deserialize_header(buf)?;
let body_buf = header.body_remainder;
// body_len prefix
if body_buf.len() < 4 {
return Err(DeserializeError::BufferTooShort);
}
let body_len =
u32::from_le_bytes([body_buf[0], body_buf[1], body_buf[2], body_buf[3]]) as usize;
let body_end = 4usize
.checked_add(body_len)
.ok_or(DeserializeError::BodyLengthMismatch)?;
if body_buf.len() < body_end {
return Err(DeserializeError::BodyLengthMismatch);
}
// proc_id_len + proc_id
let inner = &body_buf[4..body_end];
if inner.len() < 4 {
return Err(DeserializeError::BufferTooShort);
}
let proc_id_len = u32::from_le_bytes([inner[0], inner[1], inner[2], inner[3]]) as usize;
let proc_id_start = 4usize;
let proc_id_end = proc_id_start
.checked_add(proc_id_len)
.ok_or(DeserializeError::ProcIdTooLong)?;
if inner.len() < proc_id_end {
return Err(DeserializeError::BufferTooShort);
}
let procedure_id = core::str::from_utf8(&inner[proc_id_start..proc_id_end])
.map_err(|_| DeserializeError::InvalidUtf8)?;
let data = inner[proc_id_end..].to_vec();
Ok(Self {
hook_id: header.hook_id,
is_upwards_call: header.is_upwards_call,
end_hook: header.end_hook,
path: header.path.into(),
procedure_id: procedure_id.into(),
data,
})
}
}
+264
View File
@@ -0,0 +1,264 @@
use super::*;
use alloc::string::ToString;
use alloc::vec;
// ── Helpers ───────────────────────────────────────────────────────────────
fn make_packet() -> Packet {
Packet {
hook_id: 42,
is_upwards_call: true,
end_hook: false,
path: "my/service/path".to_string(),
procedure_id: "my.service.Method".to_string(),
data: vec![0xDE, 0xAD, 0xBE, 0xEF],
}
}
fn make_packet_flags(is_upwards_call: bool, end_hook: bool) -> Packet {
Packet {
is_upwards_call,
end_hook,
..make_packet()
}
}
// ── Round-trip ────────────────────────────────────────────────────────────
#[test]
fn full_round_trip() {
let packet = make_packet();
let buf = packet.serialize().unwrap();
let result = Packet::deserialize(&buf).unwrap();
assert_eq!(result.hook_id, packet.hook_id);
assert_eq!(result.is_upwards_call, packet.is_upwards_call);
assert_eq!(result.end_hook, packet.end_hook);
assert_eq!(result.path, packet.path);
assert_eq!(result.procedure_id, packet.procedure_id);
assert_eq!(result.data, packet.data);
}
#[test]
fn header_round_trip() {
let packet = make_packet();
let buf = packet.serialize().unwrap();
let header = Packet::deserialize_header(&buf).unwrap();
assert_eq!(header.hook_id, packet.hook_id);
assert_eq!(header.is_upwards_call, packet.is_upwards_call);
assert_eq!(header.end_hook, packet.end_hook);
assert_eq!(header.path, packet.path);
}
// ── Flags ─────────────────────────────────────────────────────────────────
#[test]
fn flags_both_false() {
let packet = make_packet_flags(false, false);
let buf = packet.serialize().unwrap();
let header = Packet::deserialize_header(&buf).unwrap();
assert!(!header.is_upwards_call);
assert!(!header.end_hook);
}
#[test]
fn flags_both_true() {
let packet = make_packet_flags(true, true);
let buf = packet.serialize().unwrap();
let header = Packet::deserialize_header(&buf).unwrap();
assert!(header.is_upwards_call);
assert!(header.end_hook);
}
#[test]
fn flags_upwards_only() {
let packet = make_packet_flags(true, false);
let buf = packet.serialize().unwrap();
let header = Packet::deserialize_header(&buf).unwrap();
assert!(header.is_upwards_call);
assert!(!header.end_hook);
}
#[test]
fn flags_end_hook_only() {
let packet = make_packet_flags(false, true);
let buf = packet.serialize().unwrap();
let header = Packet::deserialize_header(&buf).unwrap();
assert!(!header.is_upwards_call);
assert!(header.end_hook);
}
// ── Empty fields ──────────────────────────────────────────────────────────
#[test]
fn empty_path() {
let packet = Packet {
path: "".to_string(),
..make_packet()
};
let buf = packet.serialize().unwrap();
let header = Packet::deserialize_header(&buf).unwrap();
assert_eq!(header.path, "");
}
#[test]
fn empty_procedure_id() {
let packet = Packet {
procedure_id: "".to_string(),
..make_packet()
};
let buf = packet.serialize().unwrap();
let result = Packet::deserialize(&buf).unwrap();
assert_eq!(result.procedure_id, "");
}
#[test]
fn empty_data() {
let packet = Packet {
data: vec![],
..make_packet()
};
let buf = packet.serialize().unwrap();
let result = Packet::deserialize(&buf).unwrap();
assert_eq!(result.data, &[] as &[u8]);
}
#[test]
fn all_fields_empty() {
let packet = Packet {
hook_id: 0,
is_upwards_call: false,
end_hook: false,
path: "".to_string(),
procedure_id: "".to_string(),
data: vec![],
};
let buf = packet.serialize().unwrap();
let result = Packet::deserialize(&buf).unwrap();
assert_eq!(result.hook_id, 0);
assert_eq!(result.path, "");
assert_eq!(result.procedure_id, "");
assert_eq!(result.data, &[] as &[u8]);
}
// ── Zero-copy: borrows point into the original buffer ─────────────────────
#[test]
fn header_path_is_borrowed_from_buffer() {
let buf = make_packet().serialize().unwrap();
let header = Packet::deserialize_header(&buf).unwrap();
let path_ptr = header.path.as_ptr();
let buf_range = buf.as_ptr_range();
assert!(
buf_range.contains(&path_ptr),
"path must be a subslice of the input buffer, not a new allocation"
);
}
#[test]
fn body_remainder_is_borrowed_from_buffer() {
let buf = make_packet().serialize().unwrap();
let header = Packet::deserialize_header(&buf).unwrap();
let remainder_ptr = header.body_remainder.as_ptr();
let buf_range = buf.as_ptr_range();
assert!(
buf_range.contains(&remainder_ptr),
"body_remainder must point into the input buffer"
);
}
// ── Partial deserialization: body is untouched by header parse ────────────
#[test]
fn deserialize_header_does_not_read_body() {
let buf = make_packet().serialize().unwrap();
let header = Packet::deserialize_header(&buf).unwrap();
// Re-parse body from the remainder to confirm it's intact.
let body_buf = header.body_remainder;
let body_len =
u32::from_le_bytes([body_buf[0], body_buf[1], body_buf[2], body_buf[3]]) as usize;
assert!(
body_buf.len() >= 4 + body_len,
"body_remainder must contain the full body"
);
}
#[test]
fn can_forward_buffer_after_header_parse() {
// Simulates a router: parse the header, then forward the raw buffer
// without touching the body.
let original = make_packet().serialize().unwrap();
let header = Packet::deserialize_header(&original).unwrap();
assert_eq!(header.path, "my/service/path");
// "Forward" by deserializing the full original buffer downstream.
let forwarded = Packet::deserialize(&original).unwrap();
assert_eq!(forwarded.procedure_id, "my.service.Method");
assert_eq!(forwarded.data, &[0xDE, 0xAD, 0xBE, 0xEF]);
}
// ── Truncation / corruption ───────────────────────────────────────────────
#[test]
fn truncated_in_fixed_prefix() {
let buf = make_packet().serialize().unwrap();
// Cut inside the fixed 8-byte prefix.
assert_eq!(
Packet::deserialize_header(&buf[..4]).unwrap_err(),
DeserializeError::BufferTooShort
);
}
#[test]
fn truncated_in_path() {
let buf = make_packet().serialize().unwrap();
// Cut to just past the fixed prefix, mid-path.
assert_eq!(
Packet::deserialize_header(&buf[..9]).unwrap_err(),
DeserializeError::BufferTooShort
);
}
#[test]
fn truncated_in_body() {
let buf = make_packet().serialize().unwrap();
// Remove last byte — well into the body.
assert!(Packet::deserialize(&buf[..buf.len() - 1]).is_err());
}
#[test]
fn empty_buffer_rejected() {
assert_eq!(
Packet::deserialize_header(&[]).unwrap_err(),
DeserializeError::BufferTooShort
);
}
#[test]
fn invalid_utf8_in_path() {
let mut buf = make_packet().serialize().unwrap();
// Overwrite the first byte of the path (offset 8) with an invalid UTF-8 byte.
buf[8] = 0xFF;
assert_eq!(
Packet::deserialize_header(&buf).unwrap_err(),
DeserializeError::InvalidUtf8
);
}
#[test]
fn invalid_utf8_in_procedure_id() {
let mut buf = make_packet().serialize().unwrap();
// Find where procedure_id starts: 8 + path_len + 4 (body_len) + 4 (proc_id_len)
let path_len = u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]) as usize;
let proc_id_offset = 8 + path_len + 4 + 4;
buf[proc_id_offset] = 0xFF;
assert_eq!(
Packet::deserialize(&buf).unwrap_err(),
DeserializeError::InvalidUtf8
);
}
+1
View File
@@ -0,0 +1 @@