feat(macros): service macro to remove boilerplate code

This commit is contained in:
sevki 2024-03-29 10:47:28 +00:00 committed by Sevki
parent b8387e2b8c
commit e0a9295674
16 changed files with 1590 additions and 505 deletions

View file

@ -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
View file

@ -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"

View file

@ -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",
]

View file

@ -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
View 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
View 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");
}
}
});
}

View file

@ -1,3 +1,3 @@
[toolchain]
channel = "1.75.0"
channel = "1.75"
components = [ "rustfmt", "clippy", "llvm-tools-preview", "rust-src" ]

View file

@ -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) => {{

View file

@ -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,
}

View file

@ -4,7 +4,6 @@
pub mod messages;
pub mod wire_format;
// mod serde_9p;
pub use self::messages::*;
pub use self::wire_format::Data;

View file

@ -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;

View file

@ -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;

View file

@ -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.

View file

@ -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"

View file

@ -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(),
);
}
}

View 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)*
}
})
}