feat(macros): service macro to remove boilerplate code
This commit is contained in:
parent
b8387e2b8c
commit
e0a9295674
16 changed files with 1590 additions and 505 deletions
30
.github/workflows/rust.yml
vendored
30
.github/workflows/rust.yml
vendored
|
@ -2,19 +2,33 @@ name: Build and Test
|
|||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
branches: ["main"]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- "1.75.0"
|
||||
- "1.76.0"
|
||||
- "1.77.0"
|
||||
- "stable" # stable is 1.77.1
|
||||
- "beta" # beta
|
||||
- "nightly" # nightly
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Rust ${{ matrix.rust }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
- name: Build ${{ matrix.rust }}
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
- name: e2e tests
|
||||
run: cargo run -p e2e_tests
|
||||
|
|
189
Cargo.lock
generated
189
Cargo.lock
generated
|
@ -31,9 +31,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -115,9 +115,9 @@ checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
|||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.7.0"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f"
|
||||
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
|
@ -160,9 +160,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
|||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-rs"
|
||||
|
@ -193,9 +193,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.69"
|
||||
version = "0.3.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
|
@ -234,7 +234,7 @@ version = "0.69.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"bitflags 2.5.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools",
|
||||
|
@ -259,9 +259,18 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.2"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||
|
||||
[[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 = "bumpalo"
|
||||
|
@ -308,9 +317,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.35"
|
||||
version = "0.4.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a"
|
||||
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
|
@ -331,9 +340,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.3"
|
||||
version = "4.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813"
|
||||
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
@ -387,12 +396,30 @@ version = "0.9.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc16"
|
||||
version = "0.4.0"
|
||||
|
@ -420,6 +447,16 @@ version = "0.8.19"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
|
||||
[[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 = "cuckoofilter"
|
||||
version = "0.5.0"
|
||||
|
@ -456,6 +493,16 @@ version = "0.1.13"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[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"
|
||||
|
@ -483,6 +530,16 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
|
||||
|
||||
[[package]]
|
||||
name = "e2e_tests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
"jetstream",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.10.0"
|
||||
|
@ -600,6 +657,16 @@ dependencies = [
|
|||
"slab",
|
||||
]
|
||||
|
||||
[[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 = "genfs"
|
||||
version = "0.1.4"
|
||||
|
@ -737,9 +804,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "jetstream"
|
||||
|
@ -748,7 +815,6 @@ dependencies = [
|
|||
"anyhow",
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
"bindgen",
|
||||
"bytes",
|
||||
"clap",
|
||||
"colored",
|
||||
|
@ -761,6 +827,7 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"libc",
|
||||
"parking_lot",
|
||||
"paste",
|
||||
"pkg-config",
|
||||
"s2n-quic",
|
||||
"serde",
|
||||
|
@ -778,15 +845,19 @@ dependencies = [
|
|||
"tuple-map",
|
||||
"which 6.0.1",
|
||||
"x509-certificate",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jetstream_wire_format_derive"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"paste",
|
||||
"pretty_assertions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"sha256",
|
||||
"syn",
|
||||
]
|
||||
|
||||
|
@ -848,7 +919,7 @@ version = "0.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"bitflags 2.5.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
@ -883,9 +954,9 @@ checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
|||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
||||
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
@ -928,7 +999,7 @@ version = "0.27.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"bitflags 2.5.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
|
@ -1086,9 +1157,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
|
||||
checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
|
@ -1205,9 +1276,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.3"
|
||||
version = "1.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -1228,9 +1299,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
|
@ -1276,11 +1347,11 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.31"
|
||||
version = "0.38.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
|
||||
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"bitflags 2.5.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
|
@ -1469,9 +1540,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "s2n-tls"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d49e7ff0a2f7fa23816ba41e8cd6a04d3e06c28d6a0bd6e572702c7a8be9abd"
|
||||
checksum = "4910054601de496adaeb5688d696f15f0526c3f07359f7ab09828b9d4c8bddc1"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"hex",
|
||||
|
@ -1482,9 +1553,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "s2n-tls-sys"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7252540b5c58110aec78ebaeac0fe7268ab6f5b301e78bed9f8085b858513ef2"
|
||||
checksum = "a0c1aed56ea7a119a218d167e6b8d46f883aad63d3dfba8641e56e8464a4fbfc"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"cc",
|
||||
|
@ -1527,6 +1598,30 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha256"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"hex",
|
||||
"sha2",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
@ -1553,9 +1648,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54ac45299ccbd390721be55b412d41931911f654fa99e2cb8bfb57184b2061fe"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
|
@ -1636,9 +1731,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.1"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
|
@ -1795,9 +1890,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.36.0"
|
||||
version = "1.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
|
||||
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
@ -1915,12 +2010,24 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23d5919d7121237af683b7fa982450597b1eaa2643e597aec3b519e4e5ab3d62"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
|
|
|
@ -48,9 +48,10 @@ parking_lot = "0.12.1"
|
|||
crc16 = "0.4.0"
|
||||
slog-envlogger = "2.2.0"
|
||||
tokio-vsock = "0.5.0"
|
||||
zerocopy = { version = "0.7.32", features = ["derive", "simd"] }
|
||||
paste = "1.0.14"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.69"
|
||||
pkg-config = "0.3"
|
||||
which = "6.0.1"
|
||||
|
||||
|
@ -58,6 +59,6 @@ which = "6.0.1"
|
|||
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
members = [ "e2e_tests",
|
||||
"third_party/p9_wire_format_derive",
|
||||
]
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<img src="logo/JetStream.png" style="width: 200px">
|
||||
|
||||
# JetStream [![crates.io](https://img.shields.io/crates/v/jetstream.svg)](https://crates.io/crates/jetstream) [![docs.rs](https://docs.rs/jetstream/badge.svg)](https://docs.rs/jetstream) <!--gh actions--> ![Build Status](https://github.com/sevki/jetstream/actions/workflows/rust.yml/badge.svg) ![Build Status](https://github.com/sevki/jetstream/actions/workflows/release.yml/badge.svg)
|
||||
# JetStream
|
||||
|
||||
[![crates.io](https://img.shields.io/crates/v/jetstream.svg)](https://crates.io/crates/jetstream) [![docs.rs](https://docs.rs/jetstream/badge.svg)](https://docs.rs/jetstream) <!--gh actions--> ![Build Status](https://github.com/sevki/jetstream/actions/workflows/rust.yml/badge.svg) ![Build Status](https://github.com/sevki/jetstream/actions/workflows/release.yml/badge.svg) [![crates.io downloads](https://img.shields.io/crates/d/jetstream.svg)](https://crates.io/crates/jetstream)
|
||||
|
||||
JetStream is an RPC framework built on top of [s2n-quic](https://crates.io/crates/s2n-quic) and [p9](https://crates.io/crates/p9). It's designed to be a high performance, low latency, secure, and reliable RPC framework.
|
||||
|
||||
|
@ -28,7 +29,6 @@ JetStream is not ready for production use. It's still in the early stages of dev
|
|||
- [jsonrpc](https://www.jsonrpc.org/)
|
||||
- [tarpc](https://crates.io/crates/tarpc)
|
||||
|
||||
|
||||
## Example [test](src/server/server_tests.rs)
|
||||
|
||||
```rust
|
||||
|
@ -158,4 +158,4 @@ tokio::select! {
|
|||
|
||||
## [License](LICENSE)
|
||||
|
||||
BSD-3-Clause
|
||||
BSD-3-Clause
|
||||
|
|
12
e2e_tests/Cargo.toml
Normal file
12
e2e_tests/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "e2e_tests"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.79"
|
||||
futures = "0.3.30"
|
||||
jetstream = { version = "*", path = ".." }
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
75
e2e_tests/src/main.rs
Normal file
75
e2e_tests/src/main.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
use radar::{
|
||||
Radar, RadarProtocol, Rmessage, Tframe, Tmessage, Tversion, Version,
|
||||
PROTOCOL_VERSION,
|
||||
};
|
||||
pub use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
#[jetstream::service]
|
||||
mod radar {
|
||||
|
||||
#[derive(JetStreamWireFormat)]
|
||||
pub struct Version {
|
||||
pub msize: u32,
|
||||
pub version: String,
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
pub trait Radar {
|
||||
async fn version(&mut self, req: Version) -> Version;
|
||||
fn ping(&mut self) -> u8;
|
||||
}
|
||||
}
|
||||
struct MyRadar;
|
||||
|
||||
impl Radar for MyRadar {
|
||||
fn ping(&mut self) -> u8 {
|
||||
0
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)]
|
||||
fn version<'life0, 'async_trait>(
|
||||
&'life0 mut self,
|
||||
req: Version,
|
||||
) -> ::core::pin::Pin<
|
||||
Box<
|
||||
dyn ::core::future::Future<Output = Version>
|
||||
+ ::core::marker::Send
|
||||
+ 'async_trait,
|
||||
>,
|
||||
>
|
||||
where
|
||||
'life0: 'async_trait,
|
||||
Self: 'async_trait,
|
||||
{
|
||||
Box::pin(async move { req })
|
||||
}
|
||||
}
|
||||
|
||||
impl RadarProtocol for MyRadar {}
|
||||
|
||||
fn main() {
|
||||
let mut r = MyRadar;
|
||||
futures::executor::block_on(async {
|
||||
let ver = r
|
||||
.rpc(Tframe {
|
||||
tag: 0,
|
||||
msg: Tmessage::Version(Tversion {
|
||||
tag: 0,
|
||||
req: Version {
|
||||
msize: 0,
|
||||
version: PROTOCOL_VERSION.to_string(),
|
||||
},
|
||||
}),
|
||||
})
|
||||
.await;
|
||||
|
||||
match ver.msg {
|
||||
Rmessage::Version(v) => {
|
||||
println!("Version: {:?}", v.1.version);
|
||||
}
|
||||
_ => {
|
||||
println!("Unexpected response");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "1.75.0"
|
||||
channel = "1.75"
|
||||
components = [ "rustfmt", "clippy", "llvm-tools-preview", "rust-src" ]
|
||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -1,6 +1,7 @@
|
|||
//! <img src="https://raw.githubusercontent.com/sevki/jetstream/main/logo/JetStream.png" style="width: 200px">
|
||||
//!
|
||||
//! # JetStream [![crates.io](https://img.shields.io/crates/v/jetstream.svg)](https://crates.io/crates/jetstream) [![docs.rs](https://docs.rs/jetstream/badge.svg)](https://docs.rs/jetstream) <!--gh actions--> ![Build Status](https://github.com/sevki/jetstream/actions/workflows/rust.yml/badge.svg) ![Build Status](https://github.com/sevki/jetstream/actions/workflows/release.yml/badge.svg)
|
||||
//! # JetStream
|
||||
//! [![crates.io](https://img.shields.io/crates/v/jetstream.svg)](https://crates.io/crates/jetstream) [![docs.rs](https://docs.rs/jetstream/badge.svg)](https://docs.rs/jetstream) <!--gh actions--> ![Build Status](https://github.com/sevki/jetstream/actions/workflows/rust.yml/badge.svg) ![Build Status](https://github.com/sevki/jetstream/actions/workflows/release.yml/badge.svg) [![crates.io downloads](https://img.shields.io/crates/d/jetstream.svg)](https://crates.io/crates/jetstream)
|
||||
//!
|
||||
//!
|
||||
//! JetStream is an RPC framework built on top of [s2n-quic](https://crates.io/crates/s2n-quic) and [p9](https://crates.io/crates/p9). It's designed to be a high performance, low latency, secure, and reliable RPC framework.
|
||||
|
@ -43,6 +44,7 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate jetstream_wire_format_derive;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub mod async_wire_format;
|
||||
#[cfg(feature = "client")]
|
||||
|
@ -52,12 +54,19 @@ pub mod filesystem;
|
|||
pub mod server;
|
||||
pub mod service;
|
||||
pub mod protocol;
|
||||
|
||||
pub mod ufs;
|
||||
|
||||
pub mod log;
|
||||
|
||||
pub use jetstream_wire_format_derive::JetStreamWireFormat;
|
||||
|
||||
pub use jetstream_wire_format_derive::service;
|
||||
|
||||
pub use protocol::{Data, WireFormat, messages};
|
||||
|
||||
pub use service::Message;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! syscall {
|
||||
($e:expr) => {{
|
||||
|
|
|
@ -315,213 +315,7 @@ impl Tframe {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tversion {
|
||||
pub msize: u32,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tflush {
|
||||
pub oldtag: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Twalk {
|
||||
pub fid: u32,
|
||||
pub newfid: u32,
|
||||
pub wnames: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tread {
|
||||
pub fid: u32,
|
||||
pub offset: u64,
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Twrite {
|
||||
pub fid: u32,
|
||||
pub offset: u64,
|
||||
pub data: Data,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tclunk {
|
||||
pub fid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tremove {
|
||||
pub fid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tauth {
|
||||
pub afid: u32,
|
||||
pub uname: String,
|
||||
pub aname: String,
|
||||
pub n_uname: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tattach {
|
||||
pub fid: u32,
|
||||
pub afid: u32,
|
||||
pub uname: String,
|
||||
pub aname: String,
|
||||
pub n_uname: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tstatfs {
|
||||
pub fid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tlopen {
|
||||
pub fid: u32,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tlcreate {
|
||||
pub fid: u32,
|
||||
pub name: String,
|
||||
pub flags: u32,
|
||||
pub mode: u32,
|
||||
pub gid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tsymlink {
|
||||
pub fid: u32,
|
||||
pub name: String,
|
||||
pub symtgt: String,
|
||||
pub gid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tmknod {
|
||||
pub dfid: u32,
|
||||
pub name: String,
|
||||
pub mode: u32,
|
||||
pub major: u32,
|
||||
pub minor: u32,
|
||||
pub gid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Trename {
|
||||
pub fid: u32,
|
||||
pub dfid: u32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Treadlink {
|
||||
pub fid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tgetattr {
|
||||
pub fid: u32,
|
||||
pub request_mask: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tsetattr {
|
||||
pub fid: u32,
|
||||
pub valid: u32,
|
||||
pub mode: u32,
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
pub size: u64,
|
||||
pub atime_sec: u64,
|
||||
pub atime_nsec: u64,
|
||||
pub mtime_sec: u64,
|
||||
pub mtime_nsec: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Txattrwalk {
|
||||
pub fid: u32,
|
||||
pub newfid: u32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Txattrcreate {
|
||||
pub fid: u32,
|
||||
pub name: String,
|
||||
pub attr_size: u64,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Treaddir {
|
||||
pub fid: u32,
|
||||
pub offset: u64,
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tfsync {
|
||||
pub fid: u32,
|
||||
pub datasync: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tlock {
|
||||
pub fid: u32,
|
||||
pub type_: u8,
|
||||
pub flags: u32,
|
||||
pub start: u64,
|
||||
pub length: u64,
|
||||
pub proc_id: u32,
|
||||
pub client_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tgetlock {
|
||||
pub fid: u32,
|
||||
pub type_: u8,
|
||||
pub start: u64,
|
||||
pub length: u64,
|
||||
pub proc_id: u32,
|
||||
pub client_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tlink {
|
||||
pub dfid: u32,
|
||||
pub fid: u32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tmkdir {
|
||||
pub dfid: u32,
|
||||
pub name: String,
|
||||
pub mode: u32,
|
||||
pub gid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Trenameat {
|
||||
pub olddirfid: u32,
|
||||
pub oldname: String,
|
||||
pub newdirfid: u32,
|
||||
pub newname: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tunlinkat {
|
||||
pub dirfd: u32,
|
||||
pub name: String,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
/// A message sent from a 9P server to a 9P client in response to a request from
|
||||
/// that client. Encapsulates a full frame.
|
||||
|
@ -728,6 +522,589 @@ impl WireFormat for Rframe {
|
|||
}
|
||||
}
|
||||
|
||||
/// version -- negotiate protocol version
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tversion tag[2] msize[4] version[s]
|
||||
/// size[4] Rversion tag[2] msize[4] version[s]
|
||||
/// ```
|
||||
///
|
||||
/// version establishes the msize, which is the maximum message size inclusive of the size value that can be handled by both client and server.
|
||||
///
|
||||
/// It also establishes the protocol version. For 9P2000.L version must be the string 9P2000.L.
|
||||
///
|
||||
/// See the Plan 9 manual page for [version(5)](http://9p.io/magic/man2html/5/version).
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tversion {
|
||||
pub msize: u32,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
/// flush -- abort a message
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tflush tag[2] oldtag[2]
|
||||
/// size[4] Rflush tag[2]
|
||||
/// ```
|
||||
///
|
||||
/// flush aborts an in-flight request referenced by oldtag, if any.
|
||||
///
|
||||
/// See the Plan 9 manual page for [flush(5)](http://9p.io/magic/man2html/5/flush).
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tflush {
|
||||
pub oldtag: u16,
|
||||
}
|
||||
|
||||
/// walk -- descend a directory hierarchy
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
|
||||
/// size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
|
||||
/// ```
|
||||
///
|
||||
/// walk is used to descend a directory represented by fid using successive path elements provided in the wname array. If successful, newfid represents the new path.
|
||||
///
|
||||
/// fid can be cloned to newfid by calling walk with nwname set to zero.
|
||||
///
|
||||
/// See the Plan 9 manual page for [walk(5)](http://9p.io/magic/man2html/5/walk).
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Twalk {
|
||||
pub fid: u32,
|
||||
pub newfid: u32,
|
||||
pub wnames: Vec<String>,
|
||||
}
|
||||
|
||||
/// attach -- attach to a file tree
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]
|
||||
/// size[4] Rattach tag[2] qid[13]
|
||||
/// ```
|
||||
///
|
||||
/// attach associates the fid with the file tree rooted at aname.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tattach {
|
||||
pub fid: u32,
|
||||
pub afid: u32,
|
||||
pub uname: String,
|
||||
pub aname: String,
|
||||
pub n_uname: u32,
|
||||
}
|
||||
|
||||
/// auth -- authenticate a user
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tauth tag[2] afid[4] uname[s] aname[s]
|
||||
/// size[4] Rauth tag[2] aqid[13]
|
||||
/// ```
|
||||
///
|
||||
/// auth authenticates the user named uname to access the file tree with the root named aname.
|
||||
///
|
||||
/// afid is used as the fid in the attach message that follows auth.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tauth {
|
||||
pub afid: u32,
|
||||
pub uname: String,
|
||||
pub aname: String,
|
||||
pub n_uname: u32,
|
||||
}
|
||||
|
||||
|
||||
/// read -- read data from a file
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tread tag[2] fid[4] offset[8] count[4]
|
||||
/// size[4] Rread tag[2] count[4] data[count]
|
||||
/// ```
|
||||
///
|
||||
/// read performs I/O on the file represented by fid.
|
||||
///
|
||||
/// Under 9P2000.L, read cannot be used on directories. See [Treaddir](struct.Treaddir.html) for reading directories.
|
||||
///
|
||||
/// See the Plan 9 manual page for [read(5)](http://9p.io/magic/man2html/5/read).
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tread {
|
||||
pub fid: u32,
|
||||
pub offset: u64,
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rread {
|
||||
pub data: Data,
|
||||
}
|
||||
|
||||
/// write -- write data to a file
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Twrite tag[2] fid[4] offset[8] data[count]
|
||||
/// size[4] Rwrite tag[2] count[4]
|
||||
/// ```
|
||||
///
|
||||
/// write performs I/O on the file represented by fid.
|
||||
///
|
||||
/// See the Plan 9 manual page for [write(5)](http://9p.io/magic/man2html/5/write).
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Twrite {
|
||||
pub fid: u32,
|
||||
pub offset: u64,
|
||||
pub data: Data,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rwrite {
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
/// clunk -- remove fid
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tclunk tag[2] fid[4]
|
||||
/// size[4] Rclunk tag[2]
|
||||
/// ```
|
||||
///
|
||||
/// clunk removes the fid from the fid table.
|
||||
///
|
||||
/// See the Plan 9 manual page for [clunk(5)](http://9p.io/magic/man2html/5/clunk).
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tclunk {
|
||||
pub fid: u32,
|
||||
}
|
||||
|
||||
/// remove -- remove a file
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tremove tag[2] fid[4]
|
||||
/// size[4] Rremove tag[2]
|
||||
/// ```
|
||||
///
|
||||
/// remove removes the file represented by fid.
|
||||
///
|
||||
/// See the Plan 9 manual page for [remove(5)](http://9p.io/magic/man2html/5/remove).
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tremove {
|
||||
pub fid: u32,
|
||||
}
|
||||
|
||||
/// statfs -- get file system information
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tstatfs tag[2] fid[4]
|
||||
/// size[4] Rstatfs tag[2] type[4] bsize[4] blocks[8] bfree[8] bavail[8]
|
||||
/// files[8] ffree[8] fsid[8] namelen[4]
|
||||
/// ```
|
||||
///
|
||||
/// statfs is used to request file system information of the file system containing fid.
|
||||
/// The Rstatfs response corresponds to the fields returned by the statfs(2) system call.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tstatfs {
|
||||
pub fid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rstatfs {
|
||||
pub ty: u32,
|
||||
pub bsize: u32,
|
||||
pub blocks: u64,
|
||||
pub bfree: u64,
|
||||
pub bavail: u64,
|
||||
pub files: u64,
|
||||
pub ffree: u64,
|
||||
pub fsid: u64,
|
||||
pub namelen: u32,
|
||||
}
|
||||
|
||||
/// lopen -- open a file
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tlopen tag[2] fid[4] flags[4]
|
||||
/// size[4] Rlopen tag[2] qid[13] iounit[4]
|
||||
/// ```
|
||||
///
|
||||
/// lopen prepares fid for file I/O. The flags field has the standard open(2) values.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tlopen {
|
||||
pub fid: u32,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rlopen {
|
||||
pub qid: Qid,
|
||||
pub iounit: u32,
|
||||
}
|
||||
|
||||
/// lcreate -- create a file
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tlcreate tag[2] fid[4] name[s] flags[4] mode[4] gid[4]
|
||||
/// size[4] Rlcreate tag[2] qid[13] iounit[4]
|
||||
/// ```
|
||||
///
|
||||
/// lcreate creates a new file name in the directory represented by fid and prepares it for I/O.
|
||||
/// The flags field has the standard open(2) values.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tlcreate {
|
||||
pub fid: u32,
|
||||
pub name: String,
|
||||
pub flags: u32,
|
||||
pub mode: u32,
|
||||
pub gid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rlcreate {
|
||||
pub qid: Qid,
|
||||
pub iounit: u32,
|
||||
}
|
||||
|
||||
/// symlink -- create symlink
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tsymlink tag[2] fid[4] name[s] symtgt[s] gid[4]
|
||||
/// size[4] Rsymlink tag[2] qid[13]
|
||||
/// ```
|
||||
///
|
||||
/// symlink creates a new symbolic link name in the directory represented by fid.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tsymlink {
|
||||
pub fid: u32,
|
||||
pub name: String,
|
||||
pub symtgt: String,
|
||||
pub gid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rsymlink {
|
||||
pub qid: Qid,
|
||||
}
|
||||
|
||||
/// mknod -- create a special file
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tmknod tag[2] dfid[4] name[s] mode[4] major[4] minor[4] gid[4]
|
||||
/// size[4] Rmknod tag[2] qid[13]
|
||||
/// ```
|
||||
///
|
||||
/// mknod creates a new special file name in the directory represented by dfid.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tmknod {
|
||||
pub dfid: u32,
|
||||
pub name: String,
|
||||
pub mode: u32,
|
||||
pub major: u32,
|
||||
pub minor: u32,
|
||||
pub gid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rmknod {
|
||||
pub qid: Qid,
|
||||
}
|
||||
|
||||
/// rename -- rename a file
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Trename tag[2] fid[4] dfid[4] name[s]
|
||||
/// size[4] Rrename tag[2]
|
||||
/// ```
|
||||
///
|
||||
/// rename renames a file or directory from old name to new name in the
|
||||
/// directory represented by dfid. fid represents the file to be renamed.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Trename {
|
||||
pub fid: u32,
|
||||
pub dfid: u32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// readlink -- read symlink value
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Treadlink tag[2] fid[4]
|
||||
/// size[4] Rreadlink tag[2] target[s]
|
||||
/// ```
|
||||
///
|
||||
/// readlink reads the target of the symbolic link represented by fid.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Treadlink {
|
||||
pub fid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rreadlink {
|
||||
pub target: String,
|
||||
}
|
||||
|
||||
/// getattr -- get file attributes
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tgetattr tag[2] fid[4] request_mask[8]
|
||||
/// size[4] Rgetattr tag[2] valid[8] qid[13] mode[4] uid[4] gid[4] nlink[8]
|
||||
/// rdev[8] size[8] blksize[8] blocks[8] atime_sec[8] atime_nsec[8]
|
||||
/// mtime_sec[8] mtime_nsec[8] ctime_sec[8] ctime_nsec[8] btime_sec[8]
|
||||
/// btime_nsec[8] gen[8] data_version[8]
|
||||
/// ```
|
||||
///
|
||||
/// getattr gets attributes of the file system object represented by fid.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tgetattr {
|
||||
pub fid: u32,
|
||||
pub request_mask: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rgetattr {
|
||||
pub valid: u64,
|
||||
pub qid: Qid,
|
||||
pub mode: u32,
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
pub nlink: u64,
|
||||
pub rdev: u64,
|
||||
pub size: u64,
|
||||
pub blksize: u64,
|
||||
pub blocks: u64,
|
||||
pub atime_sec: u64,
|
||||
pub atime_nsec: u64,
|
||||
pub mtime_sec: u64,
|
||||
pub mtime_nsec: u64,
|
||||
pub ctime_sec: u64,
|
||||
pub ctime_nsec: u64,
|
||||
pub btime_sec: u64,
|
||||
pub btime_nsec: u64,
|
||||
pub gen: u64,
|
||||
pub data_version: u64,
|
||||
}
|
||||
|
||||
/// setattr -- set file attributes
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tsetattr tag[2] fid[4] valid[4] mode[4] uid[4] gid[4] size[8]
|
||||
/// atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]
|
||||
/// size[4] Rsetattr tag[2]
|
||||
/// ```
|
||||
///
|
||||
/// setattr sets attributes of the file system object represented by fid.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tsetattr {
|
||||
pub fid: u32,
|
||||
pub valid: u32,
|
||||
pub mode: u32,
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
pub size: u64,
|
||||
pub atime_sec: u64,
|
||||
pub atime_nsec: u64,
|
||||
pub mtime_sec: u64,
|
||||
pub mtime_nsec: u64,
|
||||
}
|
||||
|
||||
/// xattrwalk -- walk extended attributes
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Txattrwalk tag[2] fid[4] newfid[4] name[s]
|
||||
/// size[4] Rxattrwalk tag[2] size[8]
|
||||
/// ```
|
||||
///
|
||||
/// xattrwalk gets a new fid pointing to the extended attribute directory
|
||||
/// of the file represented by fid.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Txattrwalk {
|
||||
pub fid: u32,
|
||||
pub newfid: u32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rxattrwalk {
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
/// xattrcreate -- create an extended attribute
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Txattrcreate tag[2] fid[4] name[s] attr_size[8] flags[4]
|
||||
/// size[4] Rxattrcreate tag[2]
|
||||
/// ```
|
||||
///
|
||||
/// xattrcreate creates a new extended attribute named name of the file represented by fid.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Txattrcreate {
|
||||
pub fid: u32,
|
||||
pub name: String,
|
||||
pub attr_size: u64,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
/// readdir -- read directory entries
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Treaddir tag[2] fid[4] offset[8] count[4]
|
||||
/// size[4] Rreaddir tag[2] count[4] data[count]
|
||||
/// ```
|
||||
///
|
||||
/// readdir reads directory entries from the directory represented by fid.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Treaddir {
|
||||
pub fid: u32,
|
||||
pub offset: u64,
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rreaddir {
|
||||
pub data: Data,
|
||||
}
|
||||
|
||||
/// fsync -- synchronize file
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tfsync tag[2] fid[4] datasync[4]
|
||||
/// size[4] Rfsync tag[2]
|
||||
/// ```
|
||||
///
|
||||
/// fsync flushes any cached data and metadata for the file represented by fid to stable storage.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tfsync {
|
||||
pub fid: u32,
|
||||
pub datasync: u32,
|
||||
}
|
||||
|
||||
/// lock -- acquire or release a POSIX record lock
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tlock tag[2] fid[4] type[1] flags[4] start[8] length[8] proc_id[4] client_id[s]
|
||||
/// size[4] Rlock tag[2] status[1]
|
||||
/// ```
|
||||
///
|
||||
/// lock acquires or releases a POSIX record lock on the open file fid.
|
||||
///
|
||||
/// See the Plan 9 manual page for [lock(5)](http://9p.io/magic/man2html/5/lock).
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tlock {
|
||||
pub fid: u32,
|
||||
pub type_: u8,
|
||||
pub flags: u32,
|
||||
pub start: u64,
|
||||
pub length: u64,
|
||||
pub proc_id: u32,
|
||||
pub client_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rlock {
|
||||
pub status: u8,
|
||||
}
|
||||
|
||||
/// getlock -- test for the existence of a POSIX record lock
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tgetlock tag[2] fid[4] type[1] start[8] length[8] proc_id[4] client_id[s]
|
||||
/// size[4] Rgetlock tag[2] type[1] start[8] length[8] proc_id[4] client_id[s]
|
||||
/// ```
|
||||
///
|
||||
/// getlock tests for the existence of a POSIX record lock on the open file fid.
|
||||
///
|
||||
/// See the Plan 9 manual page for [getlock(5)](http://9p.io/magic/man2html/5/getlock).
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tgetlock {
|
||||
pub fid: u32,
|
||||
pub type_: u8,
|
||||
pub start: u64,
|
||||
pub length: u64,
|
||||
pub proc_id: u32,
|
||||
pub client_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rgetlock {
|
||||
pub type_: u8,
|
||||
pub start: u64,
|
||||
pub length: u64,
|
||||
pub proc_id: u32,
|
||||
pub client_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rlerror {
|
||||
pub ecode: u32,
|
||||
}
|
||||
|
||||
// Rerror
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rerror {
|
||||
pub ename: String,
|
||||
}
|
||||
/// link -- create hard link
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tlink tag[2] dfid[4] fid[4] name[s]
|
||||
/// size[4] Rlink tag[2]
|
||||
/// ```
|
||||
///
|
||||
/// link creates a new hard link name in the directory dfid that refers to the same file as fid.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tlink {
|
||||
pub dfid: u32,
|
||||
pub fid: u32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// mkdir -- create directory
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tmkdir tag[2] dfid[4] name[s] mode[4] gid[4]
|
||||
/// size[4] Rmkdir tag[2] qid[13]
|
||||
/// ```
|
||||
///
|
||||
/// mkdir creates a new directory name in the directory represented by dfid.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tmkdir {
|
||||
pub dfid: u32,
|
||||
pub name: String,
|
||||
pub mode: u32,
|
||||
pub gid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rmkdir {
|
||||
pub qid: Qid,
|
||||
}
|
||||
|
||||
/// renameat -- rename a file or directory
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Trenameat tag[2] olddirfid[4] oldname[s] newdirfid[4] newname[s]
|
||||
/// size[4] Rrenameat tag[2]
|
||||
/// ```
|
||||
///
|
||||
/// renameat renames a file or directory from oldname in the directory represented by
|
||||
/// olddirfid to newname in the directory represented by newdirfid.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Trenameat {
|
||||
pub olddirfid: u32,
|
||||
pub oldname: String,
|
||||
pub newdirfid: u32,
|
||||
pub newname: String,
|
||||
}
|
||||
|
||||
/// unlinkat -- unlink a file or directory
|
||||
///
|
||||
/// ```text
|
||||
/// size[4] Tunlinkat tag[2] dirfd[4] name[s] flags[4]
|
||||
/// size[4] Runlinkat tag[2]
|
||||
/// ```
|
||||
///
|
||||
/// unlinkat removes the file name from the directory represented by dirfd.
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Tunlinkat {
|
||||
pub dirfd: u32,
|
||||
pub name: String,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
/// Qid
|
||||
#[derive(Debug, Copy, Clone, JetStreamWireFormat)]
|
||||
pub struct Qid {
|
||||
pub ty: u8,
|
||||
|
@ -735,6 +1112,7 @@ pub struct Qid {
|
|||
pub path: u64,
|
||||
}
|
||||
|
||||
/// Dirent -- directory entry
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Dirent {
|
||||
pub qid: Qid,
|
||||
|
@ -754,126 +1132,12 @@ pub struct Rwalk {
|
|||
pub wqids: Vec<Qid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rread {
|
||||
pub data: Data,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rwrite {
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rauth {
|
||||
pub aqid: Qid,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rattach {
|
||||
pub qid: Qid,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rlerror {
|
||||
pub ecode: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rstatfs {
|
||||
pub ty: u32,
|
||||
pub bsize: u32,
|
||||
pub blocks: u64,
|
||||
pub bfree: u64,
|
||||
pub bavail: u64,
|
||||
pub files: u64,
|
||||
pub ffree: u64,
|
||||
pub fsid: u64,
|
||||
pub namelen: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rlopen {
|
||||
pub qid: Qid,
|
||||
pub iounit: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rlcreate {
|
||||
pub qid: Qid,
|
||||
pub iounit: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rsymlink {
|
||||
pub qid: Qid,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rmknod {
|
||||
pub qid: Qid,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rreadlink {
|
||||
pub target: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rgetattr {
|
||||
pub valid: u64,
|
||||
pub qid: Qid,
|
||||
pub mode: u32,
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
pub nlink: u64,
|
||||
pub rdev: u64,
|
||||
pub size: u64,
|
||||
pub blksize: u64,
|
||||
pub blocks: u64,
|
||||
pub atime_sec: u64,
|
||||
pub atime_nsec: u64,
|
||||
pub mtime_sec: u64,
|
||||
pub mtime_nsec: u64,
|
||||
pub ctime_sec: u64,
|
||||
pub ctime_nsec: u64,
|
||||
pub btime_sec: u64,
|
||||
pub btime_nsec: u64,
|
||||
pub gen: u64,
|
||||
pub data_version: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rxattrwalk {
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rreaddir {
|
||||
pub data: Data,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rlock {
|
||||
pub status: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rgetlock {
|
||||
pub type_: u8,
|
||||
pub start: u64,
|
||||
pub length: u64,
|
||||
pub proc_id: u32,
|
||||
pub client_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rmkdir {
|
||||
pub qid: Qid,
|
||||
}
|
||||
|
||||
// Rerror
|
||||
#[derive(Debug, JetStreamWireFormat)]
|
||||
pub struct Rerror {
|
||||
pub ename: String,
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
pub mod messages;
|
||||
pub mod wire_format;
|
||||
// mod serde_9p;
|
||||
|
||||
pub use self::messages::*;
|
||||
pub use self::wire_format::Data;
|
||||
|
|
|
@ -12,6 +12,9 @@ use std::ops::Deref;
|
|||
use std::ops::DerefMut;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
use zerocopy::{AsBytes, LittleEndian};
|
||||
|
||||
use crate::messages::Rlerror;
|
||||
|
||||
/// A type that can be encoded on the wire using the 9P protocol.
|
||||
pub trait WireFormat: std::marker::Sized + Send {
|
||||
|
@ -38,37 +41,42 @@ macro_rules! uint_wire_format_impl {
|
|||
}
|
||||
|
||||
fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
let mut buf = [0u8; mem::size_of::<$Ty>()];
|
||||
|
||||
// Encode the bytes into the buffer in little endian order.
|
||||
for idx in 0..mem::size_of::<$Ty>() {
|
||||
buf[idx] = (self >> (8 * idx)) as u8;
|
||||
}
|
||||
|
||||
writer.write_all(&buf)
|
||||
writer.write_all(&self.as_bytes())
|
||||
}
|
||||
|
||||
fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
|
||||
let mut buf = [0u8; mem::size_of::<$Ty>()];
|
||||
let mut buf = [0; mem::size_of::<$Ty>()];
|
||||
reader.read_exact(&mut buf)?;
|
||||
|
||||
// Read bytes from the buffer in little endian order.
|
||||
let mut result = 0;
|
||||
for idx in 0..mem::size_of::<$Ty>() {
|
||||
result |= (buf[idx] as $Ty) << (8 * idx);
|
||||
paste::expr! {
|
||||
let num: zerocopy::[<$Ty:snake:upper>]<LittleEndian> = zerocopy::byteorder::[<$Ty:snake:upper>]::from_bytes(buf);
|
||||
Ok(num.get())
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
uint_wire_format_impl!(u8);
|
||||
|
||||
uint_wire_format_impl!(u16);
|
||||
uint_wire_format_impl!(u32);
|
||||
uint_wire_format_impl!(u64);
|
||||
uint_wire_format_impl!(i32);
|
||||
|
||||
impl WireFormat for u8 {
|
||||
fn byte_size(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
writer.write_all(&[*self])
|
||||
}
|
||||
|
||||
fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
|
||||
let mut byte = [0u8; 1];
|
||||
reader.read_exact(&mut byte)?;
|
||||
Ok(byte[0])
|
||||
}
|
||||
}
|
||||
|
||||
// The 9P protocol requires that strings are UTF-8 encoded. The wire format is a u16
|
||||
// count |N|, encoded in little endian, followed by |N| bytes of UTF-8 data.
|
||||
impl WireFormat for String {
|
||||
|
@ -138,7 +146,6 @@ impl<T: WireFormat> WireFormat for Vec<T> {
|
|||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct Data(pub Vec<u8>);
|
||||
|
||||
|
||||
// The maximum length of a data buffer that we support. In practice the server's max message
|
||||
// size should prevent us from reading too much data so this check is mainly to ensure a
|
||||
// malicious client cannot trick us into allocating massive amounts of memory.
|
||||
|
@ -233,6 +240,84 @@ impl WireFormat for Data {
|
|||
}
|
||||
}
|
||||
|
||||
impl WireFormat for () {
|
||||
fn byte_size(&self) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
fn encode<W: Write>(&self, _writer: &mut W) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode<R: Read>(_reader: &mut R) -> io::Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn error_to_rmessage(err: &io::Error) -> Rlerror {
|
||||
let errno = if let Some(errno) = err.raw_os_error() {
|
||||
errno
|
||||
} else {
|
||||
// Make a best-effort guess based on the kind.
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => libc::ENOENT,
|
||||
io::ErrorKind::PermissionDenied => libc::EPERM,
|
||||
io::ErrorKind::ConnectionRefused => libc::ECONNREFUSED,
|
||||
io::ErrorKind::ConnectionReset => libc::ECONNRESET,
|
||||
io::ErrorKind::ConnectionAborted => libc::ECONNABORTED,
|
||||
io::ErrorKind::NotConnected => libc::ENOTCONN,
|
||||
io::ErrorKind::AddrInUse => libc::EADDRINUSE,
|
||||
io::ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL,
|
||||
io::ErrorKind::BrokenPipe => libc::EPIPE,
|
||||
io::ErrorKind::AlreadyExists => libc::EEXIST,
|
||||
io::ErrorKind::WouldBlock => libc::EWOULDBLOCK,
|
||||
io::ErrorKind::InvalidInput => libc::EINVAL,
|
||||
io::ErrorKind::InvalidData => libc::EINVAL,
|
||||
io::ErrorKind::TimedOut => libc::ETIMEDOUT,
|
||||
io::ErrorKind::WriteZero => libc::EIO,
|
||||
io::ErrorKind::Interrupted => libc::EINTR,
|
||||
io::ErrorKind::Other => libc::EIO,
|
||||
io::ErrorKind::UnexpectedEof => libc::EIO,
|
||||
_ => libc::EIO,
|
||||
}
|
||||
};
|
||||
|
||||
Rlerror {
|
||||
ecode: errno as u32,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WireFormat> WireFormat for io::Result<T> {
|
||||
fn byte_size(&self) -> u32 {
|
||||
match self {
|
||||
Ok(value) => value.byte_size(),
|
||||
Err(err) => {
|
||||
let error = error_to_rmessage(err);
|
||||
error.byte_size()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
Ok(value) => value.encode(writer),
|
||||
Err(err) => {
|
||||
let error = error_to_rmessage(err);
|
||||
error.encode(writer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
|
||||
let error = Rlerror::decode(reader)?;
|
||||
if error.ecode == 0 {
|
||||
Ok(Ok(T::decode(reader)?))
|
||||
} else {
|
||||
Err(io::Error::from_raw_os_error(error.ecode as i32))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::io::Cursor;
|
||||
|
|
|
@ -6,18 +6,44 @@ mod tests {
|
|||
server::{
|
||||
proxy::{DialQuic, Proxy},
|
||||
quic_server::QuicServer,
|
||||
},
|
||||
service::ninepecho::{self, EchoService},
|
||||
}, service::JetStreamService, messages::Rmessage,
|
||||
};
|
||||
use crate::protocol::{Rframe, Tframe, Tmessage, Tversion};
|
||||
use futures_util::Future;
|
||||
use s2n_quic::{provider::tls, Server};
|
||||
use slog_scope::debug;
|
||||
use std::{
|
||||
path::{self, Path},
|
||||
sync::Arc,
|
||||
sync::Arc, pin::Pin, error::Error,
|
||||
};
|
||||
use tokio::{io::AsyncWriteExt, sync::Barrier, net::UnixListener};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EchoService;
|
||||
|
||||
impl JetStreamService<Tframe, Rframe> for EchoService {
|
||||
fn call(
|
||||
&mut self,
|
||||
_req: Tframe,
|
||||
) -> Pin<
|
||||
Box<
|
||||
dyn Future<
|
||||
Output = Result<Rframe, Box<dyn Error + Send + Sync>>,
|
||||
> + Send,
|
||||
>,
|
||||
> {
|
||||
Box::pin(async move {
|
||||
Ok(Rframe {
|
||||
tag: 0,
|
||||
msg: Rmessage::Version(crate::protocol::Rversion {
|
||||
msize: 0,
|
||||
version: "9P2000".to_string(),
|
||||
}),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||||
async fn test_quic_server_unix_socket_proxy() {
|
||||
let _guard = slog_scope::set_global_logger(setup_logging());
|
||||
|
@ -58,7 +84,7 @@ mod tests {
|
|||
.start()
|
||||
.unwrap();
|
||||
let qsrv: QuicServer<Tframe, Rframe, EchoService> =
|
||||
QuicServer::new(ninepecho::EchoService);
|
||||
QuicServer::new(EchoService);
|
||||
debug!("Server started, waiting for barrier");
|
||||
c.wait().await;
|
||||
let _ = qsrv.serve(server).await;
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
use std::{
|
||||
error::Error,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use crate::protocol::{Rframe, Rmessage, Tframe, WireFormat};
|
||||
use crate::protocol::{Rframe, Tframe, WireFormat};
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use futures::prelude::*;
|
||||
use tower::Service;
|
||||
|
||||
pub use jetstream_wire_format_derive::JetStreamWireFormat;
|
||||
|
||||
/// Message trait for JetStream messages, which need to implement the `WireFormat` trait.
|
||||
pub trait Message: WireFormat + Send + Sync {}
|
||||
|
@ -64,85 +60,6 @@ impl<S: NinePService> JetStreamService<Tframe, Rframe> for NinePServiceImpl<S> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A static 9p service that always returns a version message.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Radar;
|
||||
|
||||
#[derive(Debug, Clone, JetStreamWireFormat)]
|
||||
struct Ping(u8);
|
||||
|
||||
impl Message for Ping {}
|
||||
|
||||
#[derive(Debug, Clone, JetStreamWireFormat)]
|
||||
struct Pong(u8);
|
||||
|
||||
impl Message for Pong {}
|
||||
|
||||
impl JetStreamService<Ping, Pong> for Radar {
|
||||
fn call(
|
||||
&mut self,
|
||||
req: Ping,
|
||||
) -> Pin<
|
||||
Box<
|
||||
dyn Future<Output = Result<Pong, Box<dyn Error + Send + Sync>>>
|
||||
+ Send,
|
||||
>,
|
||||
> {
|
||||
Box::pin(async move { Ok(Pong(req.0)) })
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ninepecho {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct EchoService;
|
||||
|
||||
impl JetStreamService<Tframe, Rframe> for EchoService {
|
||||
fn call(
|
||||
&mut self,
|
||||
_req: Tframe,
|
||||
) -> Pin<
|
||||
Box<
|
||||
dyn Future<
|
||||
Output = Result<Rframe, Box<dyn Error + Send + Sync>>,
|
||||
> + Send,
|
||||
>,
|
||||
> {
|
||||
Box::pin(async move {
|
||||
Ok(Rframe {
|
||||
tag: 0,
|
||||
msg: Rmessage::Version(crate::protocol::Rversion {
|
||||
msize: 0,
|
||||
version: "9P2000".to_string(),
|
||||
}),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Echo;
|
||||
|
||||
impl Service<bytes::Bytes> for Echo {
|
||||
type Error = Box<dyn Error + Send + Sync>;
|
||||
type Future = Pin<
|
||||
Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>,
|
||||
>;
|
||||
type Response = bytes::Bytes;
|
||||
|
||||
fn poll_ready(
|
||||
&mut self,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: bytes::Bytes) -> Self::Future {
|
||||
Box::pin(async move { Ok(req) })
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for converting types to and from a wire format.
|
||||
pub trait ConvertWireFormat: WireFormat {
|
||||
/// Converts the type to a byte representation.
|
||||
|
|
11
third_party/p9_wire_format_derive/Cargo.toml
vendored
11
third_party/p9_wire_format_derive/Cargo.toml
vendored
|
@ -1,21 +1,26 @@
|
|||
[package]
|
||||
name = "jetstream_wire_format_derive"
|
||||
version = "1.0.0"
|
||||
authors = ["The ChromiumOS Authors"]
|
||||
authors = ["Sevki <s@sevki.io>", "The ChromiumOS Authors"]
|
||||
license = "BSD-3-Clause"
|
||||
description = "Supporting proc-macro for the `p9` crate."
|
||||
repository = "https://github.com/sevki/jetstream"
|
||||
readme = "../../README.md"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
# rust analyzer doesn't understand the `quote` macro from `proc-macro2` so we
|
||||
# need to use `syn` to parse the input and then use `quote` to generate the
|
||||
syn = "2"
|
||||
syn = { version = "2.0.55", features = ["full", "extra-traits"] }
|
||||
quote = "^1"
|
||||
proc-macro2 = "^1"
|
||||
proc-macro2 = "1.0.79"
|
||||
paste = "1.0.14"
|
||||
convert_case = "0.6.0"
|
||||
sha256 = "1.5.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
sha256 = "1.5.0"
|
||||
|
|
104
third_party/p9_wire_format_derive/src/lib.rs
vendored
104
third_party/p9_wire_format_derive/src/lib.rs
vendored
|
@ -7,8 +7,6 @@
|
|||
//! This is only intended to be used from within the `p9` crate.
|
||||
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
|
||||
#[macro_use]
|
||||
|
@ -17,13 +15,10 @@ extern crate quote;
|
|||
#[macro_use]
|
||||
extern crate syn;
|
||||
|
||||
use proc_macro2::Span;
|
||||
use proc_macro2::TokenStream;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::Data;
|
||||
use syn::DeriveInput;
|
||||
use syn::Fields;
|
||||
use syn::Ident;
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
use syn::{DeriveInput, Fields, Ident, Data, spanned::Spanned};
|
||||
|
||||
mod service;
|
||||
|
||||
/// The function that derives the actual implementation.
|
||||
#[proc_macro_derive(JetStreamWireFormat)]
|
||||
|
@ -34,6 +29,14 @@ pub fn p9_wire_format(
|
|||
p9_wire_format_inner(input).into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn service(
|
||||
_attr: proc_macro::TokenStream,
|
||||
item: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
service::derive_jet_stream_protocol_impl(_attr, item)
|
||||
}
|
||||
|
||||
fn p9_wire_format_inner(input: DeriveInput) -> TokenStream {
|
||||
if !input.generics.params.is_empty() {
|
||||
return quote! {
|
||||
|
@ -56,8 +59,7 @@ fn p9_wire_format_inner(input: DeriveInput) -> TokenStream {
|
|||
use self::std::result::Result::Ok;
|
||||
|
||||
use super::#container;
|
||||
|
||||
use protocol::WireFormat;
|
||||
use super::WireFormat;
|
||||
|
||||
impl WireFormat for #container {
|
||||
fn byte_size(&self) -> u32 {
|
||||
|
@ -103,10 +105,10 @@ fn byte_size_sum(data: &Data) -> TokenStream {
|
|||
0 #(+ #fields)*
|
||||
}
|
||||
} else {
|
||||
unimplemented!();
|
||||
unimplemented!("byte_size_sum for {:?}", data.struct_token.span);
|
||||
}
|
||||
} else {
|
||||
unimplemented!();
|
||||
unimplemented!("byte_size_sum for ");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,7 +168,6 @@ fn decode_wire_format(data: &Data, container: &Ident) -> TokenStream {
|
|||
#field: #field,
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
quote! {
|
||||
#(#values)*
|
||||
|
@ -176,7 +177,7 @@ fn decode_wire_format(data: &Data, container: &Ident) -> TokenStream {
|
|||
})
|
||||
}
|
||||
} else if let Fields::Unnamed(unnamed) = &data.fields {
|
||||
let values = unnamed.unnamed.iter().enumerate().map(|(i, f)| {
|
||||
let values = unnamed.unnamed.iter().enumerate().map(|(i, _f)| {
|
||||
let index = syn::Index::from(i);
|
||||
// create a new ident that s __{index}
|
||||
let ident = Ident::new(
|
||||
|
@ -184,7 +185,7 @@ fn decode_wire_format(data: &Data, container: &Ident) -> TokenStream {
|
|||
Span::call_site(),
|
||||
);
|
||||
quote! {
|
||||
let #ident: #f = WireFormat::decode(_reader)?;
|
||||
let #ident = WireFormat::decode(_reader)?;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -201,8 +202,9 @@ fn decode_wire_format(data: &Data, container: &Ident) -> TokenStream {
|
|||
|
||||
quote! {
|
||||
#(#values)*
|
||||
|
||||
Ok(#container(
|
||||
#(#members)*
|
||||
#(#members,)*
|
||||
))
|
||||
}
|
||||
} else {
|
||||
|
@ -315,7 +317,7 @@ mod tests {
|
|||
|
||||
use super::Niijima_先輩;
|
||||
|
||||
use protocol::WireFormat;
|
||||
use super::WireFormat;
|
||||
|
||||
impl WireFormat for Niijima_先輩 {
|
||||
fn byte_size(&self) -> u32 {
|
||||
|
@ -366,4 +368,70 @@ mod tests {
|
|||
expected.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn end_to_end_unnamed() {
|
||||
let input: DeriveInput = parse_quote! {
|
||||
struct Niijima_先輩(u8, u16, u32, u64, String, Vec<String>, Nested);
|
||||
};
|
||||
|
||||
let expected = quote! {
|
||||
mod wire_format_niijima_先輩 {
|
||||
extern crate std;
|
||||
use self::std::io;
|
||||
use self::std::result::Result::Ok;
|
||||
|
||||
use super::Niijima_先輩;
|
||||
|
||||
use super::WireFormat;
|
||||
|
||||
impl WireFormat for Niijima_先輩 {
|
||||
fn byte_size(&self) -> u32 {
|
||||
0
|
||||
+ WireFormat::byte_size(&self.0)
|
||||
+ WireFormat::byte_size(&self.1)
|
||||
+ WireFormat::byte_size(&self.2)
|
||||
+ WireFormat::byte_size(&self.3)
|
||||
+ WireFormat::byte_size(&self.4)
|
||||
+ WireFormat::byte_size(&self.5)
|
||||
+ WireFormat::byte_size(&self.6)
|
||||
}
|
||||
|
||||
fn encode<W: io::Write>(&self, _writer: &mut W) -> io::Result<()> {
|
||||
WireFormat::encode(&self.0, _writer)?;
|
||||
WireFormat::encode(&self.1, _writer)?;
|
||||
WireFormat::encode(&self.2, _writer)?;
|
||||
WireFormat::encode(&self.3, _writer)?;
|
||||
WireFormat::encode(&self.4, _writer)?;
|
||||
WireFormat::encode(&self.5, _writer)?;
|
||||
WireFormat::encode(&self.6, _writer)?;
|
||||
Ok(())
|
||||
}
|
||||
fn decode<R: io::Read>(_reader: &mut R) -> io::Result<Self> {
|
||||
let __0 = WireFormat::decode(_reader)?;
|
||||
let __1= WireFormat::decode(_reader)?;
|
||||
let __2 = WireFormat::decode(_reader)?;
|
||||
let __3 = WireFormat::decode(_reader)?;
|
||||
let __4 = WireFormat::decode(_reader)?;
|
||||
let __5 = WireFormat::decode(_reader)?;
|
||||
let __6 = WireFormat::decode(_reader)?;
|
||||
Ok(Niijima_先輩(
|
||||
__0,
|
||||
__1,
|
||||
__2,
|
||||
__3,
|
||||
__4,
|
||||
__5,
|
||||
__6,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
p9_wire_format_inner(input).to_string(),
|
||||
expected.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
503
third_party/p9_wire_format_derive/src/service.rs
vendored
Normal file
503
third_party/p9_wire_format_derive/src/service.rs
vendored
Normal file
|
@ -0,0 +1,503 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use std::iter;
|
||||
use sha256::digest;
|
||||
use self::proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
Ident, Item, ItemMod,
|
||||
ItemTrait, TraitItem, parse_str,
|
||||
};
|
||||
|
||||
fn generate_msg_id(
|
||||
index: usize,
|
||||
method_name: &Ident,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let upper_cased_method_name = method_name.to_string().to_uppercase();
|
||||
let tmsg_const_name = Ident::new(
|
||||
&format!("T{}", upper_cased_method_name),
|
||||
method_name.span(),
|
||||
);
|
||||
let rmsg_const_name = Ident::new(
|
||||
&format!("R{}", upper_cased_method_name),
|
||||
method_name.span(),
|
||||
);
|
||||
let offset = 2 * index as u8;
|
||||
|
||||
quote! {
|
||||
pub const #tmsg_const_name: u8 = MESSAGE_ID_START + #offset;
|
||||
pub const #rmsg_const_name: u8 = MESSAGE_ID_START + #offset + 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_input_struct(
|
||||
request_struct_ident: &Ident,
|
||||
method_sig: &syn::Signature,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let inputs = method_sig.inputs.iter().map(|arg| match arg {
|
||||
syn::FnArg::Typed(pat) => {
|
||||
let name = pat.pat.clone();
|
||||
let ty = pat.ty.clone();
|
||||
quote! {
|
||||
pub #name: #ty
|
||||
}
|
||||
}
|
||||
syn::FnArg::Receiver(_) => quote! {},
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[derive(JetStreamWireFormat)]
|
||||
pub struct #request_struct_ident {
|
||||
pub tag: u16,
|
||||
#(#inputs)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_return_struct(
|
||||
return_struct_ident: &Ident,
|
||||
method_sig: &syn::Signature,
|
||||
) -> proc_macro2::TokenStream {
|
||||
match &method_sig.output {
|
||||
syn::ReturnType::Type(_, ty) => quote! {
|
||||
#[derive(JetStreamWireFormat)]
|
||||
pub struct #return_struct_ident (pub u16, pub #ty );
|
||||
},
|
||||
syn::ReturnType::Default => quote! {
|
||||
#[derive(JetStreamWireFormat)]
|
||||
pub struct #return_struct_ident(pub u16);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
struct IdentCased(Ident);
|
||||
|
||||
impl From<&Ident> for IdentCased {
|
||||
fn from(ident: &Ident) -> Self {
|
||||
IdentCased(ident.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl IdentCased {
|
||||
fn remove_prefix(&self) -> Self {
|
||||
let s = self.0.to_string();
|
||||
IdentCased(Ident::new(&s[1..], self.0.span()))
|
||||
}
|
||||
fn to_title_case(&self) -> Self {
|
||||
let converter =
|
||||
convert_case::Converter::new().to_case(convert_case::Case::Title);
|
||||
let converted = converter.convert(self.0.to_string());
|
||||
IdentCased(Ident::new(&converted, self.0.span()))
|
||||
}
|
||||
fn to_upper_case(&self) -> Self {
|
||||
let converter =
|
||||
convert_case::Converter::new().to_case(convert_case::Case::Upper);
|
||||
let converted = converter.convert(self.0.to_string());
|
||||
IdentCased(Ident::new(&converted, self.0.span()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IdentCased> for Ident {
|
||||
fn from(ident: IdentCased) -> Self {
|
||||
ident.0
|
||||
}
|
||||
}
|
||||
|
||||
enum Direction{Rx, Tx}
|
||||
|
||||
|
||||
fn generate_frame(
|
||||
direction: Direction,
|
||||
msgs: Vec<(Ident, proc_macro2::TokenStream)>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let enum_name = match direction {
|
||||
Direction::Rx => quote! { Rmessage },
|
||||
Direction::Tx => quote! { Tmessage },
|
||||
};
|
||||
let frame_name = match direction {
|
||||
Direction::Rx => quote! { Rframe },
|
||||
Direction::Tx => quote! { Tframe },
|
||||
};
|
||||
|
||||
let rmsg_variants = msgs.iter().map(|(ident, _)| {
|
||||
let name: IdentCased = ident.into();
|
||||
let variant_name: Ident = name.remove_prefix().to_title_case().into();
|
||||
quote! {
|
||||
#variant_name(#ident),
|
||||
}
|
||||
});
|
||||
let cloned_byte_sizes = msgs.iter().map(|(ident, _)| {
|
||||
let name: IdentCased = ident.into();
|
||||
let variant_name: Ident = name.remove_prefix().to_title_case().into();
|
||||
quote! {
|
||||
#enum_name::#variant_name(msg) => msg.byte_size()
|
||||
}
|
||||
});
|
||||
|
||||
let match_arms = msgs.iter().map(|(ident, _)| {
|
||||
let name: IdentCased = ident.into();
|
||||
let variant_name: Ident = name.remove_prefix().to_title_case().into();
|
||||
quote! {
|
||||
#enum_name::#variant_name(msg)
|
||||
}
|
||||
});
|
||||
|
||||
let encode_bodies = msgs.iter().map(|(ident, _)| {
|
||||
let name: IdentCased = ident.into();
|
||||
let variant_name: Ident = name.to_upper_case().into();
|
||||
let new_ident = Ident::new(&format!("{}", variant_name), ident.span());
|
||||
quote!{
|
||||
#new_ident,
|
||||
}
|
||||
});
|
||||
|
||||
let decode_bodies = msgs.iter().map(|(ident, _)| {
|
||||
let name: IdentCased = ident.into();
|
||||
let variant_name: Ident = name.remove_prefix().to_title_case().into();
|
||||
let const_name: Ident = name.to_upper_case().into();
|
||||
quote!{
|
||||
#const_name => Ok(#enum_name::#variant_name(WireFormat::decode(reader)?)),
|
||||
}
|
||||
});
|
||||
|
||||
let type_match_arms = std::iter::zip(match_arms.clone(), encode_bodies.clone()).map(|(arm, body)| {
|
||||
quote! {
|
||||
#arm => #body
|
||||
}
|
||||
});
|
||||
|
||||
let encode_match_arms = match_arms.clone().map(|arm| {
|
||||
quote! {
|
||||
#arm => msg.encode(writer)?,
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
quote! {
|
||||
pub enum #enum_name {
|
||||
#( #rmsg_variants )*
|
||||
}
|
||||
|
||||
pub struct #frame_name {
|
||||
pub tag: u16,
|
||||
pub msg: #enum_name,
|
||||
}
|
||||
impl WireFormat for #frame_name {
|
||||
fn byte_size(&self) -> u32 {
|
||||
let msg = &self.msg;
|
||||
let msg_size = match msg {
|
||||
#(
|
||||
#cloned_byte_sizes,
|
||||
)*
|
||||
};
|
||||
// size + type + tag + message size
|
||||
(mem::size_of::<u32>() + mem::size_of::<u8>() + mem::size_of::<u16>())
|
||||
as u32
|
||||
+ msg_size
|
||||
}
|
||||
|
||||
fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
self.byte_size().encode(writer)?;
|
||||
|
||||
let ty = match &self.msg {
|
||||
#(
|
||||
#type_match_arms
|
||||
)*
|
||||
};
|
||||
|
||||
ty.encode(writer)?;
|
||||
|
||||
self.tag.encode(writer)?;
|
||||
|
||||
match &self.msg {
|
||||
#(
|
||||
#encode_match_arms
|
||||
)*
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
|
||||
let byte_size = u32::decode(reader)?;
|
||||
|
||||
// byte_size includes the size of byte_size so remove that from the
|
||||
// expected length of the message. Also make sure that byte_size is at least
|
||||
// that long to begin with.
|
||||
if byte_size < mem::size_of::<u32>() as u32 {
|
||||
return Err(io::Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
format!("byte_size(= {}) is less than 4 bytes", byte_size),
|
||||
));
|
||||
}
|
||||
|
||||
let reader = &mut reader.take(byte_size as u64 - mem::size_of::<u32>() as u64);
|
||||
|
||||
let mut ty: u8 = WireFormat::decode(reader)?;
|
||||
|
||||
let tag: u16 = WireFormat::decode(reader)?;
|
||||
|
||||
let msg: #enum_name = Self::decode_message(reader, ty)?;
|
||||
|
||||
Ok(#frame_name { tag, msg })
|
||||
}
|
||||
}
|
||||
impl #frame_name {
|
||||
fn decode_message<R: Read>(reader: &mut R, ty: u8) -> io::Result<#enum_name> {
|
||||
match ty {
|
||||
#(
|
||||
#decode_bodies
|
||||
)*
|
||||
_ => Err(io::Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
format!("unknown message type: {}", ty),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn generate_tframe(
|
||||
tmsgs: Vec<(Ident, proc_macro2::TokenStream)>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
generate_frame(Direction::Tx, tmsgs)
|
||||
}
|
||||
|
||||
fn generate_rframe(
|
||||
rmsgs: Vec<(Ident, proc_macro2::TokenStream)>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
generate_frame(Direction::Rx, rmsgs)
|
||||
}
|
||||
|
||||
fn generate_match_arms(
|
||||
tmsgs: impl Iterator<Item = (Ident, proc_macro2::TokenStream)>,
|
||||
) -> impl Iterator<Item = proc_macro2::TokenStream> {
|
||||
tmsgs.map(|(ident, _)| {
|
||||
let name: IdentCased = (&ident).into();
|
||||
let variant_name: Ident = name.remove_prefix().to_title_case().into();
|
||||
quote! {
|
||||
Tmessage::#variant_name(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generate_jetstream_prococol(
|
||||
item: &mut ItemTrait,
|
||||
digest: String,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let trait_name: &Ident = &item.ident;
|
||||
let original_trait_name = trait_name.clone();
|
||||
let mut trait_ident: IdentCased = trait_name.into();
|
||||
trait_ident = trait_ident.to_title_case();
|
||||
let trait_name:Ident = trait_ident.into();
|
||||
// rename the trait to have a prefix
|
||||
let trait_name = Ident::new(
|
||||
&format!("{}Protocol", trait_name),
|
||||
trait_name.span(),
|
||||
);
|
||||
|
||||
let mut tmsgs = vec![];
|
||||
let mut rmsgs = vec![];
|
||||
let mut calls = vec![];
|
||||
let mut msg_ids = vec![];
|
||||
|
||||
{
|
||||
let with_calls = item
|
||||
.items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, item)| match item {
|
||||
TraitItem::Fn(method) => {
|
||||
let method_name = &method.sig.ident;
|
||||
let request_struct_ident = Ident::new(
|
||||
&format!("T{}", method_name),
|
||||
method_name.span(),
|
||||
);
|
||||
let return_struct_ident = Ident::new(
|
||||
&format!("R{}", method_name),
|
||||
method_name.span(),
|
||||
);
|
||||
|
||||
let _output_type = match &method.sig.output {
|
||||
syn::ReturnType::Type(_, ty) => quote! { #ty },
|
||||
syn::ReturnType::Default => quote! { () },
|
||||
};
|
||||
let msg_id = generate_msg_id(index, method_name);
|
||||
msg_ids.push(msg_id);
|
||||
let request_struct =
|
||||
generate_input_struct(&request_struct_ident.clone(), &method.sig);
|
||||
let return_struct =
|
||||
generate_return_struct(&return_struct_ident.clone(), &method.sig);
|
||||
|
||||
tmsgs.push((request_struct_ident.clone(),request_struct.clone()));
|
||||
rmsgs.push((return_struct_ident.clone(),return_struct.clone()));
|
||||
let has_req = method.sig.inputs.iter().count() > 1;
|
||||
let is_async = method.sig.asyncness.is_some();
|
||||
let maybe_await = if is_async { quote! { .await } } else { quote! {} };
|
||||
if has_req {
|
||||
quote! {
|
||||
#[inline]
|
||||
async fn #method_name(&mut self, tag: u16, req: #request_struct_ident) -> #return_struct_ident {
|
||||
#return_struct_ident(tag, #original_trait_name::#method_name(self, req.req)#maybe_await)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[inline]
|
||||
async fn #method_name(&mut self,tag: u16, req: #request_struct_ident)-> #return_struct_ident {
|
||||
#return_struct_ident(tag, #original_trait_name::#method_name(self)#maybe_await)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => quote! { #item },
|
||||
});
|
||||
calls.extend(with_calls);
|
||||
|
||||
let match_arms = generate_match_arms(tmsgs.clone().into_iter());
|
||||
let match_arm_bodies: Vec<proc_macro2::TokenStream> = item.items.clone().iter().map(|item| match item {
|
||||
TraitItem::Fn(method) => {
|
||||
let method_name = &method.sig.ident;
|
||||
let name: IdentCased = method_name.into();
|
||||
let variant_name: Ident = name.to_title_case().into();
|
||||
|
||||
quote! {
|
||||
{
|
||||
let msg = #trait_name::#method_name(self,req.tag, msg).await;
|
||||
Rframe{
|
||||
tag: req.tag,
|
||||
msg: Rmessage::#variant_name(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => quote! {},
|
||||
}).collect();
|
||||
let matches = std::iter::zip(match_arms, match_arm_bodies.iter()).map(|(arm, body)| {
|
||||
quote! {
|
||||
#arm => #body
|
||||
}
|
||||
});
|
||||
calls.extend(iter::once(quote! {
|
||||
#[inline]
|
||||
async fn rpc(&mut self, req:Tframe) -> Rframe {
|
||||
match req.msg {
|
||||
#(
|
||||
#matches
|
||||
)*
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
async fn handle<W: AsyncWrite + Send + Unpin, R: AsyncRead + Send + Unpin>(&mut self, reader: &mut R, writer: &mut W) -> io::Result<()> {
|
||||
let req = Tframe::decode_async(reader).await?;
|
||||
let res = self.rpc(req).await;
|
||||
res.encode_async(writer).await?;
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
}
|
||||
let tmsg_definitions = tmsgs.iter().map(|(_ident, def)| {
|
||||
quote! {
|
||||
#def
|
||||
}
|
||||
});
|
||||
|
||||
let rmsg_definitions = rmsgs.iter().map(|(_ident, def)| {
|
||||
quote! {
|
||||
#def
|
||||
}
|
||||
});
|
||||
|
||||
let tframe = generate_tframe(tmsgs.clone());
|
||||
let rframe = generate_rframe(rmsgs.clone());
|
||||
|
||||
#[allow(clippy::to_string_in_format_args)]
|
||||
let protocol_version = format!("pub const PROTOCOL_VERSION: &str = \"dev.branch.jetstream.proto/{}/{}.{}.{}-{}\";",
|
||||
original_trait_name.to_string().to_lowercase(),
|
||||
env!("CARGO_PKG_VERSION_MAJOR"),
|
||||
env!("CARGO_PKG_VERSION_MINOR"),
|
||||
env!("CARGO_PKG_VERSION_PATCH"),
|
||||
digest[0..8].to_string()
|
||||
);
|
||||
|
||||
// make a const with the digest
|
||||
let digest = format!("pub const DIGEST: &str = \"{}\";", digest);
|
||||
let digest = parse_str::<Item>(digest.as_str()).unwrap();
|
||||
let protocol_version = parse_str::<Item>(protocol_version.as_str()).unwrap();
|
||||
quote!(
|
||||
const MESSAGE_ID_START: u8 = 101;
|
||||
|
||||
#digest
|
||||
|
||||
#protocol_version
|
||||
|
||||
#(
|
||||
#msg_ids
|
||||
)*
|
||||
|
||||
#(
|
||||
#tmsg_definitions
|
||||
)*
|
||||
|
||||
#(
|
||||
#rmsg_definitions
|
||||
)*
|
||||
|
||||
#tframe
|
||||
|
||||
#rframe
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait #trait_name: #original_trait_name + Send + Sync {
|
||||
#(
|
||||
#calls
|
||||
)*
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
pub fn derive_jet_stream_protocol_impl(
|
||||
_attr: TokenStream,
|
||||
item: TokenStream,
|
||||
) -> TokenStream {
|
||||
let mut item_mod: ItemMod = parse_macro_input!(item as ItemMod);
|
||||
let module_name = item_mod.ident.clone();
|
||||
|
||||
// get all the original items in the module
|
||||
let original_mod = item_mod.clone();
|
||||
let original_content = original_mod.content.as_ref().expect("module content");
|
||||
let original_items = &original_content.1;
|
||||
let p =quote! { #original_mod }.to_string();
|
||||
let dig: String = digest(p);
|
||||
|
||||
let transformed_items = item_mod
|
||||
.content
|
||||
.as_mut()
|
||||
.expect("module content")
|
||||
.1
|
||||
.iter_mut()
|
||||
.map(| trait_item| match trait_item {
|
||||
Item::Trait(trait_item) => generate_jetstream_prococol(trait_item, dig.clone()),
|
||||
_ => quote! { },
|
||||
});
|
||||
// Construct the final output TokenStream
|
||||
TokenStream::from(quote! {
|
||||
mod #module_name {
|
||||
pub use async_trait::async_trait;
|
||||
use std::io;
|
||||
pub use jetstream::{Message, WireFormat, JetStreamWireFormat, async_wire_format::AsyncWireFormatExt};
|
||||
pub use std::mem;
|
||||
pub use std::io::{Read, Write, ErrorKind};
|
||||
pub use std::future::Future;
|
||||
pub use std::pin::Pin;
|
||||
pub use super::*;
|
||||
|
||||
#(#transformed_items)*
|
||||
|
||||
#(#original_items)*
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue