diff --git a/.gitignore b/.gitignore index dd0cb92..2a1546a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ test_gen_project test_types_gen_project test_map_gen_project +test_grpc_project diff --git a/Cargo.lock b/Cargo.lock index f34f795..2d6307e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,7 +53,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -64,21 +64,131 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys", + "windows-sys 0.61.2", ] +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower 0.5.3", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + [[package]] name = "bumpalo" version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + [[package]] name = "cast" version = "0.3.0" @@ -260,12 +370,49 @@ dependencies = [ "log", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + [[package]] name = "futures-task" version = "0.3.32" @@ -284,6 +431,36 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.7.1" @@ -295,6 +472,18 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "heck" version = "0.5.0" @@ -307,6 +496,126 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2 0.6.3", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + [[package]] name = "is-terminal" version = "0.4.17" @@ -315,7 +624,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -381,18 +690,50 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -420,6 +761,55 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -469,6 +859,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -478,6 +877,29 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "protos" version = "0.1.0" @@ -491,6 +913,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "rayon" version = "1.12.0" @@ -511,6 +963,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.12.3" @@ -564,6 +1025,19 @@ dependencies = [ [[package]] name = "roto-runtime" version = "0.1.0" +dependencies = [ + "bytes", +] + +[[package]] +name = "roto-tonic" +version = "0.1.0" +dependencies = [ + "bytes", + "prost", + "roto-runtime", + "tonic", +] [[package]] name = "rustversion" @@ -580,6 +1054,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.228" @@ -623,12 +1103,48 @@ dependencies = [ "zmij", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + [[package]] name = "slab" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "strsim" version = "0.11.1" @@ -646,6 +1162,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "test_grpc_project" +version = "0.1.0" +dependencies = [ + "bytes", + "prost", + "roto-runtime", + "roto-tonic", + "tokio", + "tokio-stream", + "tonic", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -656,6 +1191,171 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2 0.5.10", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -678,6 +1378,21 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasm-bindgen" version = "0.2.120" @@ -739,7 +1454,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -748,6 +1463,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -757,6 +1481,70 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "zerocopy" version = "0.8.48" diff --git a/Cargo.toml b/Cargo.toml index 44e1d9c..d4336c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "codegen", "protos", "benches", + "roto-tonic", "test_grpc_project", ] exclude = [ diff --git a/TASKS.md b/TASKS.md new file mode 100644 index 0000000..7e158a7 --- /dev/null +++ b/TASKS.md @@ -0,0 +1,44 @@ +# Tasks for Tonic Integration + +This document outlines the steps required to integrate the `roto` protobuf library with the `tonic` gRPC framework. + +## Goals +- Provide a `tonic::codec::Codec` implementation for `roto` messages. +- Enable zero-allocation decoding of gRPC requests and responses. +- Support efficient encoding of gRPC messages using `roto`'s `Builder` pattern. +- Generate `tonic`-compatible service traits and client/server boilerplate via `protoc-gen-roto`. + +## 1. Runtime Changes (`roto_runtime`) +- [x] Add `bytes` as a dependency to `roto_runtime`. +- [x] Modify `ProtoBuilder` to support writing to `bytes::BufMut` or provide a specialized `BufMut` based builder to facilitate integration with `tonic::codec::EncodeBuf`. +- [x] Design and implement "Owned" message support: + - [x] Create a mechanism (likely via codegen) to generate structs that hold `bytes::Bytes` and the field offsets (e.g., `OwnedMyMessage`). + - [x] These structs will serve as the owned types required by `tonic`'s `Codec`. + - [x] Add a method to convert an `OwnedMyMessage` into a zero-allocation `Reader` (e.g., `reader(&self) -> MyMessage<'_>`). + +## 2. Tonic Codec Implementation +- [x] Implement `tonic::codec::Codec` for `roto` messages. +- [x] Implement `tonic::codec::Decoder` for `roto` messages: + - [x] The decoder should take a `DecodeBuf` and produce an `OwnedMyMessage`. + - [x] It must perform the initial scan of the buffer to populate the field offsets. +- [x] Implement `tonic::codec::Encoder` for `roto` messages: + - [x] The encoder should take an `OwnedMyMessage` and write its internal `bytes::Bytes` to the `EncodeBuf`. + - [x] Since `roto` responses are built using `Builder` directly into a buffer, the `Encoder` will primarily handle copying these pre-built buffers. + +## 3. Code Generation Extensions (`protoc-gen-roto`) +- [x] Update the generator to produce `OwnedMyMessage` structs in addition to the `Reader` and `Builder` structs. +- [x] Generate the `OwnedMyMessage::new(data: bytes::Bytes)` method for initial decoding. +- [x] Implement gRPC service generation: + - [x] Generate `tonic`-compatible service traits (using `#[tonic::async_trait]`). + - [x] Generate client and server boilerplate that uses `OwnedMyMessage` as the request and response types. + - [x] Ensure the generated code correctly maps protobuf services to Rust traits. + +## 4. Testing and Validation +- [x] Create a test gRPC project using the updated `protoc-gen-roto`. +- [ ] Implement a sample gRPC service with: + - [ ] A unary call. + - [ ] A server-streaming call. + - [ ] A client-streaming call. + - [ ] A bidirectional-streaming call. +- [ ] Verify that the integration is zero-allocation on the reading path. +- [ ] Perform benchmark tests to compare performance with `prost`. diff --git a/codegen/src/generator.rs b/codegen/src/generator.rs index 336530d..72ec47a 100644 --- a/codegen/src/generator.rs +++ b/codegen/src/generator.rs @@ -1,6 +1,6 @@ use crate::google::protobuf::descriptor::{ DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, FileDescriptorProto, - FileDescriptorSet, MessageOptions, OneofDescriptorProto, + FileDescriptorSet, MessageOptions, MethodDescriptorProto, OneofDescriptorProto, ServiceDescriptorProto, }; use roto_runtime::ProtoAccessor; use std::collections::{HashMap, HashSet}; @@ -434,6 +434,25 @@ fn write_message(msg_proto: &DescriptorProto, output: &mut String) { output.push_str(&format!(" pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {{\n self.builder.finish()\n }}\n}}\n\n")); + output.push_str(&format!("pub struct Owned{} {{\n", msg_name)); + output.push_str(" pub data: bytes::Bytes,\n"); + output.push_str("}\n\n"); + + output.push_str(&format!("impl roto_runtime::RotoOwned for Owned{} {{\n", msg_name)); + output.push_str(&format!(" fn reader(&self) -> {}<'_> {{\n", msg_name)); + output.push_str(&format!(" {}::new(&self.data).expect(\"failed to create reader\")\n", msg_name)); + output.push_str(" }\n"); + output.push_str("}\n\n"); + + output.push_str(&format!("impl roto_runtime::RotoMessage for Owned{} {{\n", msg_name)); + output.push_str(" fn decode(buf: bytes::Bytes) -> Self {\n"); + output.push_str(&format!(" Owned{} {{ data: buf }}\n", msg_name)); + output.push_str(" }\n\n"); + output.push_str(" fn bytes(&self) -> bytes::Bytes {\n"); + output.push_str(" self.data.clone()\n"); + output.push_str(" }\n"); + output.push_str("}\n\n"); + let mut nested_enums = Vec::new(); for e_res in msg_proto.enum_type() { if let Ok((e, _)) = e_res { @@ -524,9 +543,13 @@ pub fn generate_rust_code( let mut output = String::new(); output.push_str("// @generated by protoc-gen-roto — do not edit\n"); - output.push_str("#![allow(unused_imports)]\n\n"); + output.push_str("#[allow(unused_imports)]\n\n"); output.push_str("use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator};\n"); - output.push_str("use std::str;\n\n"); + output.push_str("use std::str;\n"); + output.push_str("use bytes::Bytes;\n"); + output.push_str("use tonic::{Request, Response, Status};\n"); + output.push_str("use tokio_stream::Stream;\n"); + output.push_str("use std::pin::Pin;\n\n"); for dep_res in file_proto.dependency() { let (dep_data, _) = dep_res.expect("Failed to iterate dependency"); @@ -553,6 +576,15 @@ pub fn generate_rust_code( &mut output, ); } + + // Services + for svc_res in file_proto.service() { + let (svc_data, _) = svc_res.expect("Failed to iterate service"); + write_service( + &ServiceDescriptorProto::new(svc_data).expect("Failed to parse ServiceDescriptorProto"), + &mut output, + ); + } generated_files.push((rust_file_name, output)); } @@ -608,3 +640,44 @@ pub fn generate_rust_code( generated_files } + +fn write_service(svc_proto: &ServiceDescriptorProto, output: &mut String) { + let svc_name = to_pascal_case(svc_proto.name().unwrap()); + output.push_str(&format!("#[tonic::async_trait]\npub trait {}: Send + Sync + 'static {{\n", svc_name)); + + for method_res in svc_proto.method() { + let (method_data, _) = method_res.expect("Failed to iterate method"); + let method_proto = MethodDescriptorProto::new(method_data).expect("Failed to parse MethodDescriptorProto"); + let method_name = to_snake_case(method_proto.name().unwrap()); + + let input_full_name = method_proto.input_type().unwrap(); + let output_full_name = method_proto.output_type().unwrap(); + + let input_type = input_full_name.split('.').last().unwrap(); + let output_type = output_full_name.split('.').last().unwrap(); + + let input_owned = format!("Owned{}", input_type); + let output_owned = format!("Owned{}", output_type); + + let client_streaming = method_proto.client_streaming().unwrap_or(false); + let server_streaming = method_proto.server_streaming().unwrap_or(false); + + let req_type = if client_streaming { + format!("Request>", input_owned) + } else { + format!("Request<{}>", input_owned) + }; + + let resp_type = if server_streaming { + format!("Response> + Send>>>", output_owned) + } else { + format!("Response<{}>", output_owned) + }; + + output.push_str(&format!( + " async fn {}(&self, request: {}) -> Result<{}, Status>;\n", + method_name, req_type, resp_type + )); + } + output.push_str("}\n\n"); +} diff --git a/roto-tonic/Cargo.toml b/roto-tonic/Cargo.toml new file mode 100644 index 0000000..9b13476 --- /dev/null +++ b/roto-tonic/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "roto-tonic" +version = "0.1.0" +edition = "2024" + +[dependencies] +roto-runtime = { path = "../runtime" } +tonic = "0.12" +bytes = "1.7" +prost = "0.13" diff --git a/roto-tonic/src/lib.rs b/roto-tonic/src/lib.rs new file mode 100644 index 0000000..9e17ff0 --- /dev/null +++ b/roto-tonic/src/lib.rs @@ -0,0 +1,72 @@ +use std::marker::PhantomData; +use tonic::codec::{Codec, Decoder, Encoder, DecodeBuf, EncodeBuf}; +use bytes::{Buf, BufMut}; +use roto_runtime::RotoMessage; + +pub struct RotoCodec { + _phantom: PhantomData<(T, U)>, +} + +impl Default for RotoCodec { + fn default() -> Self { + Self { + _phantom: PhantomData, + } + } +} + +impl Codec for RotoCodec +where + T: RotoMessage + Send + 'static, + U: RotoMessage + Send + 'static, +{ + type Encode = U; + type Decode = T; + type Encoder = RotoEncoder; + type Decoder = RotoDecoder; + + fn encoder(&mut self) -> Self::Encoder { + RotoEncoder(PhantomData) + } + + fn decoder(&mut self) -> Self::Decoder { + RotoDecoder(PhantomData) + } +} + +pub struct RotoEncoder(PhantomData); + +impl Encoder for RotoEncoder +where + U: RotoMessage, +{ + type Item = U; + type Error = tonic::Status; + + fn encode(&mut self, message: Self::Item, buf: &mut EncodeBuf<'_>) -> Result<(), Self::Error> { + buf.put_slice(&message.bytes()); + Ok(()) + } +} + +pub struct RotoDecoder(PhantomData); + +impl Decoder for RotoDecoder +where + T: RotoMessage, +{ + type Item = T; + type Error = tonic::Status; + + fn decode(&mut self, buf: &mut DecodeBuf<'_>) -> Result, Self::Error> { + if buf.remaining() == 0 { + return Ok(None); + } + + let bytes = buf.copy_to_bytes(buf.remaining()); + match T::decode(bytes) { + Ok(msg) => Ok(Some(msg)), + Err(e) => Err(tonic::Status::internal(format!("Roto decode error: {}", e))), + } + } +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 29c59cd..8fc0e66 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] +bytes = "1.7" diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 55d46bb..1e675f3 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1,4 +1,5 @@ use std::fmt; +use bytes::BufMut; pub struct MapFieldIterator<'a> { inner: RepeatedFieldIterator<'a>, @@ -54,8 +55,17 @@ impl std::error::Error for RotoError {} pub type Result = std::result::Result; +pub trait RotoOwned { + type Reader<'a> where Self: 'a; + fn reader(&self) -> Self::Reader<'_>; +} + +pub trait RotoMessage: Sized { + fn decode(buf: bytes::Bytes) -> Result; + fn bytes(&self) -> bytes::Bytes; +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] pub enum WireType { Varint = 0, Fixed64 = 1, @@ -818,3 +828,86 @@ impl<'a> ProtoBuilder<'a> { Ok(&mut self.buf[..self.pos]) } } + +pub struct BufMutBuilder<'a, B: BufMut> { + buf: &'a mut B, +} + +impl<'a, B: BufMut> BufMutBuilder<'a, B> { + pub fn new(buf: &'a mut B) -> Self { + Self { buf } + } + + fn write_tag(&mut self, field_number: u32, wire_type: WireType) -> Result<()> { + let mut temp = [0u8; 10]; + let len = Tag::encode(field_number, wire_type, &mut temp)?; + self.buf.put_slice(&temp[..len]); + Ok(()) + } + + pub fn write_varint(&mut self, field_number: u32, value: u64) -> Result<()> { + self.write_tag(field_number, WireType::Varint)?; + let mut temp = [0u8; 10]; + let len = write_varint(value, &mut temp)?; + self.buf.put_slice(&temp[..len]); + Ok(()) + } + + pub fn write_int32(&mut self, field_number: u32, value: i32) -> Result<()> { + self.write_varint(field_number, value as u64) + } + + pub fn write_string(&mut self, field_number: u32, value: &str) -> Result<()> { + self.write_tag(field_number, WireType::LengthDelimited)?; + let bytes = value.as_bytes(); + let mut len_buf = [0u8; 10]; + let len_len = write_varint(bytes.len() as u64, &mut len_buf)?; + self.buf.put_slice(&len_buf[..len_len]); + self.buf.put_slice(bytes); + Ok(()) + } + + pub fn write_fixed32(&mut self, field_number: u32, value: u32) -> Result<()> { + self.write_tag(field_number, WireType::Fixed32)?; + self.buf.put_slice(&value.to_le_bytes()); + Ok(()) + } + + pub fn write_fixed64(&mut self, field_number: u32, value: u64) -> Result<()> { + self.write_tag(field_number, WireType::Fixed64)?; + self.buf.put_slice(&value.to_le_bytes()); + Ok(()) + } + + pub fn write_bytes(&mut self, field_number: u32, value: &[u8]) -> Result<()> { + self.write_tag(field_number, WireType::LengthDelimited)?; + let mut len_buf = [0u8; 10]; + let len_len = write_varint(value.len() as u64, &mut len_buf)?; + self.buf.put_slice(&len_buf[..len_len]); + self.buf.put_slice(value); + Ok(()) + } + + pub fn write_raw(&mut self, raw_bytes: &[u8]) -> Result<()> { + self.buf.put_slice(raw_bytes); + Ok(()) + } + + pub fn write_map_entry( + &mut self, + field_number: u32, + key_encoded: &[u8], + value_encoded: &[u8], + ) -> Result<()> { + let entry_len = key_encoded.len() + value_encoded.len(); + self.write_tag(field_number, WireType::LengthDelimited)?; + + let mut len_buf = [0u8; 10]; + let len_len = write_varint(entry_len as u64, &mut len_buf)?; + self.buf.put_slice(&len_buf[..len_len]); + + self.buf.put_slice(key_encoded); + self.buf.put_slice(value_encoded); + Ok(()) + } +}