Compare commits

..

34 Commits

Author SHA1 Message Date
openhands 6ac13ff43e Make code generator self-contained by integrating runtime support 2026-05-26 05:11:41 +00:00
openhands 6376abd412 Implement the requested changes in the generator modules.
Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-26 04:15:02 +00:00
openhands de6af24565 Refactor codegen/src/generator.rs into a modular directory structure and fix compilation errors.
Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-25 04:11:57 +00:00
charles 6085ef3923 More generated files 2026-05-20 08:12:38 -07:00
charles f03e1afdbf Regenerate protobuf files 2026-05-20 08:09:11 -07:00
charles 7e43e09f66 Clean up generated code and implement strip_boilerplate
Remove redundant newlines and duplicate headers from generated files
and prevent the creation of empty sub-modules. Implement the
`strip_boilerplate` function to remove generated headers and
attributes, including a comprehensive test suite.
2026-05-19 23:08:05 -07:00
charles 117cbf812b Introduce alloc feature for optional allocation
Wrap heap-allocated types and service generation in the `alloc` feature
flag to support environments without a memory allocator.
2026-05-19 21:55:18 -07:00
charles 6910f11d69 Add no_std test example and update alloc gating
Update codegen to make `bytes` imports conditional on the `alloc`
feature and add a test example for the `thumbv7em-none-eabihf`
target.
2026-05-18 08:43:22 -07:00
charles fa4d8cca83 Support no_std in roto-runtime
Add #![no_std] to the runtime crate and introduce optional std and
alloc features. Update the code generator to be compatible and add a
no_std_test example. Remove the generator binary.
2026-05-17 19:55:44 -07:00
charles 956993d1d0 Split service and proto gen 2026-05-17 18:53:00 -07:00
charles b2c5639338 add: grpc_bench 2026-05-17 16:45:44 -07:00
charles 33f3e58f74 Use original method names for gRPC paths
Stop converting method names to snake_case when generating the gRPC
service paths to maintain compatibility with protobuf definitions.
2026-05-17 10:44:07 -07:00
charles 89455190b1 Many fixes later 2026-05-17 00:43:21 -07:00
charles b11b068345 Update crate references in codegen tests
Update imports to use `roto_tonic` instead of `crate` for BufferPool
and StatusBody. Remove the broad replacement of `crate` with `roto`
and add error output to `test_map_build.rs`.
2026-05-16 19:45:46 -07:00
charles 56fc787f7a Use roto_tonic types in hello_world example
Fix the client's poll_ready implementation to properly propagate
readiness.
2026-05-16 17:24:46 -07:00
charles 43dcfabcdc Include test case and auto generated code 2026-05-16 16:57:12 -07:00
charles 2202548ae5 Support gRPC packages in generated code
Include package names in the `NamedService` identifier and request
paths. Change generated imports to use `crate` and add `tokio` and
`tokio-stream` dependencies to `roto-tonic`.
2026-05-16 16:57:01 -07:00
charles 809a0d844c Update StatusBody to support gRPC trailers
Change StatusBody from a tuple struct to a struct containing both data
and trailers. Update the codegen to use the new StatusBody::new
constructor to specify gRPC status codes.

Also remove the temp_test_project.
2026-05-15 18:57:15 -07:00
charles db89c9842a Add http dependency to helloworld build test 2026-05-15 14:25:16 -07:00
charles 546b74149e Fix code generator and add compilation test
Correct module paths and accessor usage in the generator to ensure
generated code builds. Add a test that verifies this by compiling the
generated code in a temporary project.
2026-05-15 14:13:18 -07:00
charles 372eab94d4 Fix path resolution in codegen build tests
Use absolute paths for temporary project directories and dependencies
to improve reliability. Update the generated Cargo.toml with additional
dependencies and remove the offline flag from cargo build.
2026-05-15 11:00:50 -07:00
charles a3fece24fc Update Orchestrator persona to use subagent tool 2026-05-15 10:52:07 -07:00
charles cc82e990ba Update generated code build test
Use absolute paths for the test project, add roto-tonic and other
dependencies, and capture build output on failure.
2026-05-15 10:49:56 -07:00
charles a9fef01950 Update orchestrator persona instructions
Require the orchestrator to tell subagents which persona they are and
provide the path to the persona file.
2026-05-15 10:30:21 -07:00
charles da7ba47505 Fix generated code and update example integration
Resolve `Result` ambiguity and lifetime issues in generated services.
Use `file_stem` for proto filenames. Make `StatusBody` public in
`roto-tonic` and update the `hello_world` build process.
2026-05-15 10:29:54 -07:00
charles 00b3dcd9a6 Add agent personas for task orchestration
Define roles and workflows for the Orchestrator, Researcher,
Coordinator, and Codemonkey agents, and ignore the artifacts directory.
2026-05-15 09:38:50 -07:00
charles 08be61966c Fix stuff 2026-05-13 23:08:21 -07:00
charles db2bf1bffd Implement buffer pooling in hello_world example
Introduce BufferPool to reuse BytesMut allocations for requests and
responses. Update AGENTS.md to require build and test success.
2026-05-13 09:45:27 -07:00
charles dfdcd8ae46 Add README for hello_world example 2026-05-12 14:16:27 -07:00
charles daa42d2d07 Update dependencies in codegen build tests
Bump bytes version to 1.7 and synchronize dependencies across all
generated Cargo.toml files in codegen tests.
2026-05-12 14:03:28 -07:00
charles 804ff3ead0 Checkpoint for gRPC implementation 2026-05-12 13:44:53 -07:00
charles 02a0b0d908 Initial commit of AI generated slop 2026-05-11 22:31:04 -07:00
charles 17ab0d1670 Update generated protobuf code 2026-05-11 17:43:56 -07:00
charles 792f2d5f5d Ensure we use roto_runtime rather than crate for error types 2026-05-11 17:43:25 -07:00
63 changed files with 26218 additions and 5506 deletions
+5
View File
@@ -2,3 +2,8 @@
test_gen_project test_gen_project
test_types_gen_project test_types_gen_project
test_map_gen_project test_map_gen_project
test_grpc_project
artifacts/
temp_test_project/
output_svc/
output_proto/
+3
View File
@@ -0,0 +1,3 @@
[submodule "grpc_bench"]
path = grpc_bench
url = https://github.com/Lesnyrumcajs/grpc_bench.git
+2
View File
@@ -7,6 +7,8 @@ you should be able to work without user assistance.
If you are writing code, write tests first. The tests must pass for your work to be complete. If you are writing code, write tests first. The tests must pass for your work to be complete.
Before considering a task complete, make sure that all target build, and all tests suceed.
## Special instructions ## Special instructions
### Fork ### Fork
Generated
+1158 -5
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -4,8 +4,17 @@ members = [
"codegen", "codegen",
"protos", "protos",
"benches", "benches",
"roto-tonic",
"examples/hello_world",
"examples/no_std_test",
] ]
exclude = [ exclude = [
"test_gen_project" "test_gen_project"
] ]
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
+12 -12
View File
@@ -8,8 +8,11 @@ Instead of deserializing binary protobuf data into Rust structs, roto scans a me
construction — recording the byte offset of each field — then reads fields on demand directly from construction — recording the byte offset of each field — then reads fields on demand directly from
the original bytes. No heap allocation, no data copying, no full deserialization upfront. the original bytes. No heap allocation, no data copying, no full deserialization upfront.
Writing works the same way: you provide a fixed buffer and a builder writes fields directly into it, It also provides a first-class integration with the `tonic` gRPC framework via the `roto-tonic` crate,
returning a slice of the bytes written. enabling zero-allocation request/response processing.
Writing works the same way: you provide a fixed buffer (or a `bytes::BufMut`) and a builder writes
fields directly into it, returning a slice of the bytes written.
## Design ## Design
@@ -28,10 +31,15 @@ This will generate a file, src/hackers.rs.
## Generated code ## Generated code
For each protobuf message roto generates two types: For each protobuf message roto generates three types:
- **Reader struct** `MessageName<'a>` — borrows the original byte slice, zero-copy. - **Reader struct** `MessageName<'a>` — borrows the original byte slice, zero-copy.
- **Builder struct** `MessageNameBuilder<'b>` — writes into a caller-provided `&mut [u8]`. - **Builder struct** `MessageNameBuilder<'b>` — writes into a caller-provided `&mut [u8]` or `BufMut`.
- **Owned struct** `OwnedMessageName` — owns the byte buffer and implements `RotoOwned`, providing a bridge to the `Reader`.
For each protobuf service, roto generates:
- **Service Trait** `ServiceName` — a `tonic`-compatible async trait for gRPC service implementations.
Nested message types are placed in a `pub mod message_name { ... }` module (snake_case of the Nested message types are placed in a `pub mod message_name { ... }` module (snake_case of the
parent message name) within the same generated file. parent message name) within the same generated file.
@@ -314,12 +322,4 @@ The goal is to validate roto's implementation against the Proto3 specification.
### Unsupported Features ### Unsupported Features
- **Reserved Fields**: `reserved` statements are ignored. - **Reserved Fields**: `reserved` statements are ignored.
- **Services**: `service` and `rpc` definitions are ignored.
- **Options**: Field and message options are ignored. - **Options**: Field and message options are ignored.
### Tasks
- [x] Analyze `roto/codegen` to determine which protobuf constructs are supported during code generation.
- [x] Analyze `roto/runtime` to determine which wire types and protobuf types are supported during reading and writing.
- [x] Compare findings with the Proto3 spec (https://protobuf.dev/reference/protobuf/proto3-spec/).
- [x] Document supported and unsupported features in the README.
+44
View File
@@ -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`.
+291 -108
View File
@@ -1,13 +1,13 @@
// @generated by protoc-gen-roto — do not edit // @generated by protoc-gen-roto — do not edit
#![allow(unused_imports)] #[allow(unused_imports)]
use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};
use crate::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator}; use core::str;
use std::str; #[cfg(feature = "alloc")]
use bytes::{Bytes, BytesMut, Buf, BufMut};
use crate::google::protobuf::descriptor; use crate::google::protobuf::descriptor;
pub struct Version<'a> { pub struct Version<'a> {
accessor: crate::ProtoAccessor<'a>, accessor: roto_runtime::ProtoAccessor<'a>,
major_offset: Option<usize>, major_offset: Option<usize>,
minor_offset: Option<usize>, minor_offset: Option<usize>,
patch_offset: Option<usize>, patch_offset: Option<usize>,
@@ -15,8 +15,8 @@ pub struct Version<'a> {
} }
impl<'a> Version<'a> { impl<'a> Version<'a> {
pub fn new(data: &'a [u8]) -> crate::Result<Self> { pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = crate::ProtoAccessor::new(data)?; let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut major_offset = None; let mut major_offset = None;
let mut minor_offset = None; let mut minor_offset = None;
let mut patch_offset = None; let mut patch_offset = None;
@@ -38,38 +38,62 @@ suffix_offset,
}) })
} }
pub fn major(&self) -> crate::Result<i32> { pub fn major(&self) -> roto_runtime::Result<i32> {
let offset = self.major_offset.ok_or(crate::RotoError::FieldNotFound)?; let offset = self.major_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn minor(&self) -> crate::Result<i32> { pub fn major_or_default(&self) -> roto_runtime::Result<i32> {
let offset = self.minor_offset.ok_or(crate::RotoError::FieldNotFound)?; self.major().or(Ok(0))
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation)
} }
pub fn patch(&self) -> crate::Result<i32> { pub fn has_major(&self) -> bool { self.major_offset.is_some() }
let offset = self.patch_offset.ok_or(crate::RotoError::FieldNotFound)?;
pub fn minor(&self) -> roto_runtime::Result<i32> {
let offset = self.minor_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn suffix(&self) -> crate::Result<&'a str> { pub fn minor_or_default(&self) -> roto_runtime::Result<i32> {
let offset = self.suffix_offset.ok_or(crate::RotoError::FieldNotFound)?; self.minor().or(Ok(0))
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation)
} }
pub fn raw_fields(&self) -> crate::RawFieldIterator<'a> { pub fn has_minor(&self) -> bool { self.minor_offset.is_some() }
pub fn patch(&self) -> roto_runtime::Result<i32> {
let offset = self.patch_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn patch_or_default(&self) -> roto_runtime::Result<i32> {
self.patch().or(Ok(0))
}
pub fn has_patch(&self) -> bool { self.patch_offset.is_some() }
pub fn suffix(&self) -> roto_runtime::Result<&'a str> {
let offset = self.suffix_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn suffix_or_default(&self) -> roto_runtime::Result<&'a str> {
self.suffix().or(Ok(""))
}
pub fn has_suffix(&self) -> bool { self.suffix_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields() self.accessor.raw_fields()
} }
} }
pub struct VersionBuilder<'b> { pub struct VersionBuilder<'b> {
builder: crate::ProtoBuilder<'b>, builder: roto_runtime::ProtoBuilder<'b>,
major_written: bool, major_written: bool,
minor_written: bool, minor_written: bool,
patch_written: bool, patch_written: bool,
@@ -79,7 +103,7 @@ pub struct VersionBuilder<'b> {
impl<'b> VersionBuilder<'b> { impl<'b> VersionBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> VersionBuilder<'_> { pub fn builder(buf: &mut [u8]) -> VersionBuilder<'_> {
VersionBuilder { VersionBuilder {
builder: crate::ProtoBuilder::new(buf), builder: roto_runtime::ProtoBuilder::new(buf),
major_written: false, major_written: false,
minor_written: false, minor_written: false,
patch_written: false, patch_written: false,
@@ -87,32 +111,32 @@ impl<'b> VersionBuilder<'b> {
} }
} }
pub fn major(mut self, value: i32) -> crate::Result<Self> { pub fn major(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(1, value)?; self.builder.write_int32(1, value)?;
self.major_written = true; self.major_written = true;
Ok(self) Ok(self)
} }
pub fn minor(mut self, value: i32) -> crate::Result<Self> { pub fn minor(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(2, value)?; self.builder.write_int32(2, value)?;
self.minor_written = true; self.minor_written = true;
Ok(self) Ok(self)
} }
pub fn patch(mut self, value: i32) -> crate::Result<Self> { pub fn patch(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(3, value)?; self.builder.write_int32(3, value)?;
self.patch_written = true; self.patch_written = true;
Ok(self) Ok(self)
} }
pub fn suffix(mut self, value: &str) -> crate::Result<Self> { pub fn suffix(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(4, value)?; self.builder.write_string(4, value)?;
self.suffix_written = true; self.suffix_written = true;
Ok(self) Ok(self)
} }
pub fn with(mut self, msg: &Version<'_>) -> crate::Result<Self> { pub fn with(mut self, msg: &Version<'_>) -> roto_runtime::Result<Self> {
for item in msg.raw_fields() { for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?; let (field_number, raw_bytes) = item?;
let is_written = match field_number { let is_written = match field_number {
1 => self.major_written, 1 => self.major_written,
@@ -128,13 +152,37 @@ impl<'b> VersionBuilder<'b> {
Ok(self) Ok(self)
} }
pub fn finish(self) -> crate::Result<&'b mut [u8]> { pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish() self.builder.finish()
} }
} }
#[cfg(feature = "alloc")]
pub struct OwnedVersion {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedVersion {
type Reader<'a> = Version<'a>;
fn reader(&self) -> Version<'_> {
Version::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedVersion {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedVersion { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct CodeGeneratorRequest<'a> { pub struct CodeGeneratorRequest<'a> {
accessor: crate::ProtoAccessor<'a>, accessor: roto_runtime::ProtoAccessor<'a>,
file_to_generate_start: Option<usize>, file_to_generate_start: Option<usize>,
file_to_generate_end: Option<usize>, file_to_generate_end: Option<usize>,
parameter_offset: Option<usize>, parameter_offset: Option<usize>,
@@ -146,8 +194,8 @@ pub struct CodeGeneratorRequest<'a> {
} }
impl<'a> CodeGeneratorRequest<'a> { impl<'a> CodeGeneratorRequest<'a> {
pub fn new(data: &'a [u8]) -> crate::Result<Self> { pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = crate::ProtoAccessor::new(data)?; let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut file_to_generate_start = None; let mut file_to_generate_start = None;
let mut file_to_generate_end = None; let mut file_to_generate_end = None;
let mut parameter_offset = None; let mut parameter_offset = None;
@@ -184,47 +232,59 @@ compiler_version_offset,
}) })
} }
pub fn file_to_generate(&self) -> crate::RepeatedFieldIterator<'a> { pub fn file_to_generate(&self) -> roto_runtime::RepeatedFieldIterator<'a> {
match (self.file_to_generate_start, self.file_to_generate_end) { match (self.file_to_generate_start, self.file_to_generate_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(1, start, end), (Some(start), Some(end)) => self.accessor.iter_repeated_range(1, start, end),
_ => self.accessor.iter_repeated(1), _ => self.accessor.iter_repeated(1),
} }
} }
pub fn parameter(&self) -> crate::Result<&'a str> { pub fn parameter(&self) -> roto_runtime::Result<&'a str> {
let offset = self.parameter_offset.ok_or(crate::RotoError::FieldNotFound)?; let offset = self.parameter_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn proto_file(&self) -> crate::RepeatedFieldIterator<'a> { pub fn parameter_or_default(&self) -> roto_runtime::Result<&'a str> {
self.parameter().or(Ok(""))
}
pub fn has_parameter(&self) -> bool { self.parameter_offset.is_some() }
pub fn proto_file(&self) -> roto_runtime::RepeatedFieldIterator<'a> {
match (self.proto_file_start, self.proto_file_end) { match (self.proto_file_start, self.proto_file_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end), (Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end),
_ => self.accessor.iter_repeated(15), _ => self.accessor.iter_repeated(15),
} }
} }
pub fn source_file_descriptors(&self) -> crate::RepeatedFieldIterator<'a> { pub fn source_file_descriptors(&self) -> roto_runtime::RepeatedFieldIterator<'a> {
match (self.source_file_descriptors_start, self.source_file_descriptors_end) { match (self.source_file_descriptors_start, self.source_file_descriptors_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(17, start, end), (Some(start), Some(end)) => self.accessor.iter_repeated_range(17, start, end),
_ => self.accessor.iter_repeated(17), _ => self.accessor.iter_repeated(17),
} }
} }
pub fn compiler_version(&self) -> crate::Result<&'a [u8]> { pub fn compiler_version(&self) -> roto_runtime::Result<&'a [u8]> {
let offset = self.compiler_version_offset.ok_or(crate::RotoError::FieldNotFound)?; let offset = self.compiler_version_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes) Ok(bytes)
} }
pub fn raw_fields(&self) -> crate::RawFieldIterator<'a> { pub fn compiler_version_or_default(&self) -> roto_runtime::Result<&'a [u8]> {
self.compiler_version().or(Ok(&[]))
}
pub fn has_compiler_version(&self) -> bool { self.compiler_version_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields() self.accessor.raw_fields()
} }
} }
pub struct CodeGeneratorRequestBuilder<'b> { pub struct CodeGeneratorRequestBuilder<'b> {
builder: crate::ProtoBuilder<'b>, builder: roto_runtime::ProtoBuilder<'b>,
file_to_generate_written: bool, file_to_generate_written: bool,
parameter_written: bool, parameter_written: bool,
proto_file_written: bool, proto_file_written: bool,
@@ -235,7 +295,7 @@ pub struct CodeGeneratorRequestBuilder<'b> {
impl<'b> CodeGeneratorRequestBuilder<'b> { impl<'b> CodeGeneratorRequestBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> CodeGeneratorRequestBuilder<'_> { pub fn builder(buf: &mut [u8]) -> CodeGeneratorRequestBuilder<'_> {
CodeGeneratorRequestBuilder { CodeGeneratorRequestBuilder {
builder: crate::ProtoBuilder::new(buf), builder: roto_runtime::ProtoBuilder::new(buf),
file_to_generate_written: false, file_to_generate_written: false,
parameter_written: false, parameter_written: false,
proto_file_written: false, proto_file_written: false,
@@ -244,38 +304,38 @@ impl<'b> CodeGeneratorRequestBuilder<'b> {
} }
} }
pub fn file_to_generate(mut self, value: &str) -> crate::Result<Self> { pub fn file_to_generate(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?; self.builder.write_string(1, value)?;
self.file_to_generate_written = true; self.file_to_generate_written = true;
Ok(self) Ok(self)
} }
pub fn parameter(mut self, value: &str) -> crate::Result<Self> { pub fn parameter(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(2, value)?; self.builder.write_string(2, value)?;
self.parameter_written = true; self.parameter_written = true;
Ok(self) Ok(self)
} }
pub fn proto_file(mut self, value: &[u8]) -> crate::Result<Self> { pub fn proto_file(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(15, value)?; self.builder.write_bytes(15, value)?;
self.proto_file_written = true; self.proto_file_written = true;
Ok(self) Ok(self)
} }
pub fn source_file_descriptors(mut self, value: &[u8]) -> crate::Result<Self> { pub fn source_file_descriptors(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(17, value)?; self.builder.write_bytes(17, value)?;
self.source_file_descriptors_written = true; self.source_file_descriptors_written = true;
Ok(self) Ok(self)
} }
pub fn compiler_version(mut self, value: &[u8]) -> crate::Result<Self> { pub fn compiler_version(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(3, value)?; self.builder.write_bytes(3, value)?;
self.compiler_version_written = true; self.compiler_version_written = true;
Ok(self) Ok(self)
} }
pub fn with(mut self, msg: &CodeGeneratorRequest<'_>) -> crate::Result<Self> { pub fn with(mut self, msg: &CodeGeneratorRequest<'_>) -> roto_runtime::Result<Self> {
for item in msg.raw_fields() { for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?; let (field_number, raw_bytes) = item?;
let is_written = match field_number { let is_written = match field_number {
1 => self.file_to_generate_written, 1 => self.file_to_generate_written,
@@ -292,13 +352,37 @@ impl<'b> CodeGeneratorRequestBuilder<'b> {
Ok(self) Ok(self)
} }
pub fn finish(self) -> crate::Result<&'b mut [u8]> { pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish() self.builder.finish()
} }
} }
#[cfg(feature = "alloc")]
pub struct OwnedCodeGeneratorRequest {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedCodeGeneratorRequest {
type Reader<'a> = CodeGeneratorRequest<'a>;
fn reader(&self) -> CodeGeneratorRequest<'_> {
CodeGeneratorRequest::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedCodeGeneratorRequest {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedCodeGeneratorRequest { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct CodeGeneratorResponse<'a> { pub struct CodeGeneratorResponse<'a> {
accessor: crate::ProtoAccessor<'a>, accessor: roto_runtime::ProtoAccessor<'a>,
error_offset: Option<usize>, error_offset: Option<usize>,
supported_features_offset: Option<usize>, supported_features_offset: Option<usize>,
minimum_edition_offset: Option<usize>, minimum_edition_offset: Option<usize>,
@@ -308,8 +392,8 @@ pub struct CodeGeneratorResponse<'a> {
} }
impl<'a> CodeGeneratorResponse<'a> { impl<'a> CodeGeneratorResponse<'a> {
pub fn new(data: &'a [u8]) -> crate::Result<Self> { pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = crate::ProtoAccessor::new(data)?; let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut error_offset = None; let mut error_offset = None;
let mut supported_features_offset = None; let mut supported_features_offset = None;
let mut minimum_edition_offset = None; let mut minimum_edition_offset = None;
@@ -338,45 +422,69 @@ file_start, file_end,
}) })
} }
pub fn error(&self) -> crate::Result<&'a str> { pub fn error(&self) -> roto_runtime::Result<&'a str> {
let offset = self.error_offset.ok_or(crate::RotoError::FieldNotFound)?; let offset = self.error_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn supported_features(&self) -> crate::Result<u32> { pub fn error_or_default(&self) -> roto_runtime::Result<&'a str> {
let offset = self.supported_features_offset.ok_or(crate::RotoError::FieldNotFound)?; self.error().or(Ok(""))
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as u32).map_err(|_| crate::RotoError::WireFormatViolation)
} }
pub fn minimum_edition(&self) -> crate::Result<i32> { pub fn has_error(&self) -> bool { self.error_offset.is_some() }
let offset = self.minimum_edition_offset.ok_or(crate::RotoError::FieldNotFound)?;
pub fn supported_features(&self) -> roto_runtime::Result<u32> {
let offset = self.supported_features_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) roto_runtime::read_varint(bytes).map(|(v, _)| v as u32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn maximum_edition(&self) -> crate::Result<i32> { pub fn supported_features_or_default(&self) -> roto_runtime::Result<u32> {
let offset = self.maximum_edition_offset.ok_or(crate::RotoError::FieldNotFound)?; self.supported_features().or(Ok(0))
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation)
} }
pub fn file(&self) -> crate::RepeatedFieldIterator<'a> { pub fn has_supported_features(&self) -> bool { self.supported_features_offset.is_some() }
pub fn minimum_edition(&self) -> roto_runtime::Result<i32> {
let offset = self.minimum_edition_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn minimum_edition_or_default(&self) -> roto_runtime::Result<i32> {
self.minimum_edition().or(Ok(0))
}
pub fn has_minimum_edition(&self) -> bool { self.minimum_edition_offset.is_some() }
pub fn maximum_edition(&self) -> roto_runtime::Result<i32> {
let offset = self.maximum_edition_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn maximum_edition_or_default(&self) -> roto_runtime::Result<i32> {
self.maximum_edition().or(Ok(0))
}
pub fn has_maximum_edition(&self) -> bool { self.maximum_edition_offset.is_some() }
pub fn file(&self) -> roto_runtime::RepeatedFieldIterator<'a> {
match (self.file_start, self.file_end) { match (self.file_start, self.file_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end), (Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end),
_ => self.accessor.iter_repeated(15), _ => self.accessor.iter_repeated(15),
} }
} }
pub fn raw_fields(&self) -> crate::RawFieldIterator<'a> { pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields() self.accessor.raw_fields()
} }
} }
pub struct CodeGeneratorResponseBuilder<'b> { pub struct CodeGeneratorResponseBuilder<'b> {
builder: crate::ProtoBuilder<'b>, builder: roto_runtime::ProtoBuilder<'b>,
error_written: bool, error_written: bool,
supported_features_written: bool, supported_features_written: bool,
minimum_edition_written: bool, minimum_edition_written: bool,
@@ -387,7 +495,7 @@ pub struct CodeGeneratorResponseBuilder<'b> {
impl<'b> CodeGeneratorResponseBuilder<'b> { impl<'b> CodeGeneratorResponseBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> CodeGeneratorResponseBuilder<'_> { pub fn builder(buf: &mut [u8]) -> CodeGeneratorResponseBuilder<'_> {
CodeGeneratorResponseBuilder { CodeGeneratorResponseBuilder {
builder: crate::ProtoBuilder::new(buf), builder: roto_runtime::ProtoBuilder::new(buf),
error_written: false, error_written: false,
supported_features_written: false, supported_features_written: false,
minimum_edition_written: false, minimum_edition_written: false,
@@ -396,38 +504,38 @@ impl<'b> CodeGeneratorResponseBuilder<'b> {
} }
} }
pub fn error(mut self, value: &str) -> crate::Result<Self> { pub fn error(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?; self.builder.write_string(1, value)?;
self.error_written = true; self.error_written = true;
Ok(self) Ok(self)
} }
pub fn supported_features(mut self, value: u64) -> crate::Result<Self> { pub fn supported_features(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(2, value)?; self.builder.write_varint(2, value)?;
self.supported_features_written = true; self.supported_features_written = true;
Ok(self) Ok(self)
} }
pub fn minimum_edition(mut self, value: i32) -> crate::Result<Self> { pub fn minimum_edition(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(3, value)?; self.builder.write_int32(3, value)?;
self.minimum_edition_written = true; self.minimum_edition_written = true;
Ok(self) Ok(self)
} }
pub fn maximum_edition(mut self, value: i32) -> crate::Result<Self> { pub fn maximum_edition(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(4, value)?; self.builder.write_int32(4, value)?;
self.maximum_edition_written = true; self.maximum_edition_written = true;
Ok(self) Ok(self)
} }
pub fn file(mut self, value: &[u8]) -> crate::Result<Self> { pub fn file(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(15, value)?; self.builder.write_bytes(15, value)?;
self.file_written = true; self.file_written = true;
Ok(self) Ok(self)
} }
pub fn with(mut self, msg: &CodeGeneratorResponse<'_>) -> crate::Result<Self> { pub fn with(mut self, msg: &CodeGeneratorResponse<'_>) -> roto_runtime::Result<Self> {
for item in msg.raw_fields() { for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?; let (field_number, raw_bytes) = item?;
let is_written = match field_number { let is_written = match field_number {
1 => self.error_written, 1 => self.error_written,
@@ -444,11 +552,35 @@ impl<'b> CodeGeneratorResponseBuilder<'b> {
Ok(self) Ok(self)
} }
pub fn finish(self) -> crate::Result<&'b mut [u8]> { pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish() self.builder.finish()
} }
} }
#[cfg(feature = "alloc")]
pub struct OwnedCodeGeneratorResponse {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedCodeGeneratorResponse {
type Reader<'a> = CodeGeneratorResponse<'a>;
fn reader(&self) -> CodeGeneratorResponse<'_> {
CodeGeneratorResponse::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedCodeGeneratorResponse {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedCodeGeneratorResponse { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub mod code_generator_response { pub mod code_generator_response {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)] #[repr(i32)]
@@ -470,7 +602,7 @@ impl Feature {
} }
pub struct File<'a> { pub struct File<'a> {
accessor: crate::ProtoAccessor<'a>, accessor: roto_runtime::ProtoAccessor<'a>,
name_offset: Option<usize>, name_offset: Option<usize>,
insertion_point_offset: Option<usize>, insertion_point_offset: Option<usize>,
content_offset: Option<usize>, content_offset: Option<usize>,
@@ -478,8 +610,8 @@ pub struct File<'a> {
} }
impl<'a> File<'a> { impl<'a> File<'a> {
pub fn new(data: &'a [u8]) -> crate::Result<Self> { pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = crate::ProtoAccessor::new(data)?; let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut name_offset = None; let mut name_offset = None;
let mut insertion_point_offset = None; let mut insertion_point_offset = None;
let mut content_offset = None; let mut content_offset = None;
@@ -501,38 +633,62 @@ generated_code_info_offset,
}) })
} }
pub fn name(&self) -> crate::Result<&'a str> { pub fn name(&self) -> roto_runtime::Result<&'a str> {
let offset = self.name_offset.ok_or(crate::RotoError::FieldNotFound)?; let offset = self.name_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn insertion_point(&self) -> crate::Result<&'a str> { pub fn name_or_default(&self) -> roto_runtime::Result<&'a str> {
let offset = self.insertion_point_offset.ok_or(crate::RotoError::FieldNotFound)?; self.name().or(Ok(""))
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation)
} }
pub fn content(&self) -> crate::Result<&'a str> { pub fn has_name(&self) -> bool { self.name_offset.is_some() }
let offset = self.content_offset.ok_or(crate::RotoError::FieldNotFound)?;
pub fn insertion_point(&self) -> roto_runtime::Result<&'a str> {
let offset = self.insertion_point_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn generated_code_info(&self) -> crate::Result<&'a [u8]> { pub fn insertion_point_or_default(&self) -> roto_runtime::Result<&'a str> {
let offset = self.generated_code_info_offset.ok_or(crate::RotoError::FieldNotFound)?; self.insertion_point().or(Ok(""))
}
pub fn has_insertion_point(&self) -> bool { self.insertion_point_offset.is_some() }
pub fn content(&self) -> roto_runtime::Result<&'a str> {
let offset = self.content_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn content_or_default(&self) -> roto_runtime::Result<&'a str> {
self.content().or(Ok(""))
}
pub fn has_content(&self) -> bool { self.content_offset.is_some() }
pub fn generated_code_info(&self) -> roto_runtime::Result<&'a [u8]> {
let offset = self.generated_code_info_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes) Ok(bytes)
} }
pub fn raw_fields(&self) -> crate::RawFieldIterator<'a> { pub fn generated_code_info_or_default(&self) -> roto_runtime::Result<&'a [u8]> {
self.generated_code_info().or(Ok(&[]))
}
pub fn has_generated_code_info(&self) -> bool { self.generated_code_info_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields() self.accessor.raw_fields()
} }
} }
pub struct FileBuilder<'b> { pub struct FileBuilder<'b> {
builder: crate::ProtoBuilder<'b>, builder: roto_runtime::ProtoBuilder<'b>,
name_written: bool, name_written: bool,
insertion_point_written: bool, insertion_point_written: bool,
content_written: bool, content_written: bool,
@@ -542,7 +698,7 @@ pub struct FileBuilder<'b> {
impl<'b> FileBuilder<'b> { impl<'b> FileBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> FileBuilder<'_> { pub fn builder(buf: &mut [u8]) -> FileBuilder<'_> {
FileBuilder { FileBuilder {
builder: crate::ProtoBuilder::new(buf), builder: roto_runtime::ProtoBuilder::new(buf),
name_written: false, name_written: false,
insertion_point_written: false, insertion_point_written: false,
content_written: false, content_written: false,
@@ -550,32 +706,32 @@ impl<'b> FileBuilder<'b> {
} }
} }
pub fn name(mut self, value: &str) -> crate::Result<Self> { pub fn name(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?; self.builder.write_string(1, value)?;
self.name_written = true; self.name_written = true;
Ok(self) Ok(self)
} }
pub fn insertion_point(mut self, value: &str) -> crate::Result<Self> { pub fn insertion_point(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(2, value)?; self.builder.write_string(2, value)?;
self.insertion_point_written = true; self.insertion_point_written = true;
Ok(self) Ok(self)
} }
pub fn content(mut self, value: &str) -> crate::Result<Self> { pub fn content(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(15, value)?; self.builder.write_string(15, value)?;
self.content_written = true; self.content_written = true;
Ok(self) Ok(self)
} }
pub fn generated_code_info(mut self, value: &[u8]) -> crate::Result<Self> { pub fn generated_code_info(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(16, value)?; self.builder.write_bytes(16, value)?;
self.generated_code_info_written = true; self.generated_code_info_written = true;
Ok(self) Ok(self)
} }
pub fn with(mut self, msg: &File<'_>) -> crate::Result<Self> { pub fn with(mut self, msg: &File<'_>) -> roto_runtime::Result<Self> {
for item in msg.raw_fields() { for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?; let (field_number, raw_bytes) = item?;
let is_written = match field_number { let is_written = match field_number {
1 => self.name_written, 1 => self.name_written,
@@ -591,10 +747,37 @@ impl<'b> FileBuilder<'b> {
Ok(self) Ok(self)
} }
pub fn finish(self) -> crate::Result<&'b mut [u8]> { pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish() self.builder.finish()
} }
} }
#[cfg(feature = "alloc")]
pub struct OwnedFile {
pub data: bytes::Bytes,
} }
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedFile {
type Reader<'a> = File<'a>;
fn reader(&self) -> File<'_> {
File::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedFile {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedFile { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
}
use crate::google::protobuf::descriptor;
File diff suppressed because it is too large Load Diff
+456 -294
View File
File diff suppressed because it is too large Load Diff
+9 -1
View File
@@ -4,7 +4,15 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
roto-runtime = { path = "../runtime" }
roto-tonic = { path = "../roto-tonic" }
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
log = "0.4" log = "0.4"
env_logger = "0.11" env_logger = "0.11"
bytes = "1.7"
http-body = "1.0"
http-body-util = "0.1"
tower = "0.4"
tonic = "0.12"
tokio-stream = "0.1"
futures-util = "0.3"
Binary file not shown.
@@ -1,5 +1,5 @@
use clap::Parser; use clap::Parser;
use roto_codegen::generator::generate_rust_code; use roto_codegen::generator::generate_protobuf_code;
use roto_codegen::google::protobuf::descriptor::FileDescriptorSet; use roto_codegen::google::protobuf::descriptor::FileDescriptorSet;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
@@ -29,7 +29,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let data = fs::read(&args.input)?; let data = fs::read(&args.input)?;
let set = FileDescriptorSet::new(&data).expect("Failed to parse FileDescriptorSet"); let set = FileDescriptorSet::new(&data).expect("Failed to parse FileDescriptorSet");
let files = generate_rust_code(&set, args.files.as_deref(), true); let files = generate_protobuf_code(&set, args.files.as_deref(), true);
for (filename, content) in files { for (filename, content) in files {
let path = args.output.join(filename); let path = args.output.join(filename);
+2 -2
View File
@@ -5,7 +5,7 @@ use roto_codegen::google::protobuf::compiler::plugin::{
CodeGeneratorRequest, CodeGeneratorResponseBuilder, code_generator_response::FileBuilder, CodeGeneratorRequest, CodeGeneratorResponseBuilder, code_generator_response::FileBuilder,
}; };
use roto_codegen::google::protobuf::descriptor::FileDescriptorSet; use roto_codegen::google::protobuf::descriptor::FileDescriptorSet;
// use roto_runtime::ProtoBuilder; // use roto_codegen::runtime::ProtoBuilder;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
fn main() { fn main() {
@@ -58,7 +58,7 @@ fn handle_request(
// Write length as varint // Write length as varint
let len = file_data.len() as u64; let len = file_data.len() as u64;
let mut len_buf = [0u8; 10]; let mut len_buf = [0u8; 10];
let len_size = roto_runtime::write_varint(len, &mut len_buf).map_err(|e| { let len_size = roto_codegen::runtime::write_varint(len, &mut len_buf).map_err(|e| {
error!("Failed to write varint length: {:?}", e); error!("Failed to write varint length: {:?}", e);
e e
})?; })?;
+42
View File
@@ -0,0 +1,42 @@
use clap::Parser;
use roto_codegen::generator::generate_service_code;
use roto_codegen::google::protobuf::descriptor::FileDescriptorSet;
use std::fs;
use std::path::PathBuf;
#[derive(Parser)]
#[command(
author,
version,
about = "Generates Rust gRPC service code from a protobuf descriptor set"
)]
struct Args {
/// Path to the descriptor set file (.desc)
#[arg(short, long)]
input: PathBuf,
/// Path to the output directory
#[arg(short, long)]
output: PathBuf,
/// Files to generate. If omitted, all files are generated.
#[arg(short, long, value_delimiter = ',')]
files: Option<Vec<String>>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
let data = fs::read(&args.input)?;
let set = FileDescriptorSet::new(&data).expect("Failed to parse FileDescriptorSet");
let files = generate_service_code(&set, args.files.as_deref(), true);
for (filename, content) in files {
let path = args.output.join(filename);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(path, content)?;
}
Ok(())
}
@@ -1,108 +1,13 @@
use crate::google::protobuf::descriptor::{ use crate::google::protobuf::descriptor::{DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, MessageOptions, OneofDescriptorProto};
DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, FileDescriptorProto, use crate::google::protobuf::descriptor::FileDescriptorSet;
FileDescriptorSet, MessageOptions, OneofDescriptorProto, use crate::generator::types::map_type_to_rust_builder;
};
use roto_runtime::ProtoAccessor;
use std::collections::{HashMap, HashSet};
use std::str;
pub fn to_pascal_case(s: &str) -> String { use crate::runtime::ProtoAccessor;
s.split('_') use crate::generator::utils::{to_pascal_case, to_snake_case};
.map(|word| { use crate::generator::types::map_type_to_rust_accessor;
let mut chars = word.chars();
match chars.next() {
None => String::new(),
Some(f) => f.to_uppercase().collect::<String>() + chars.as_str(),
}
})
.collect()
}
pub fn to_snake_case(s: &str) -> String { pub fn write_enum(enum_proto: &EnumDescriptorProto, output: &mut String) {
let mut result = String::new();
for (i, c) in s.chars().enumerate() {
if c.is_uppercase() {
if i > 0 {
result.push('_');
}
result.push(c.to_ascii_lowercase());
} else {
result.push(c);
}
}
result
}
fn map_type_to_rust_accessor(field_type: i32, label: i32, is_map: bool) -> (String, String, String) {
if label == 3 {
// LABEL_REPEATED
let iterator_type = if is_map {
"roto_runtime::MapFieldIterator<'a>"
} else {
"roto_runtime::RepeatedFieldIterator<'a>"
};
return (
iterator_type.to_string(),
"".to_string(), // Not used for repeated fields in the same way
"".to_string(), // Not used for repeated fields
);
}
match field_type {
9 => (
"&'a str".to_string(),
"str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation)".to_string(),
"\"\"".to_string(),
), // TYPE_STRING
1 => (
"f64".to_string(),
"Ok(f64::from_le_bytes(bytes.try_into().map_err(|_| crate::RotoError::WireFormatViolation)?))".to_string(),
"0.0".to_string(),
), // TYPE_DOUBLE
2 => (
"f32".to_string(),
"Ok(f32::from_le_bytes(bytes.try_into().map_err(|_| crate::RotoError::WireFormatViolation)?))".to_string(),
"0.0".to_string(),
), // TYPE_FLOAT
3 | 5 | 15 | 17 => (
"i32".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // INT/SINT/SFIXED 32
4 | 6 | 13 => (
"u32".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as u32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // UINT/FIXED 32
16 | 18 => (
"i64".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as i64).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // SINT/SFIXED 64
7 | 14 => (
"u64".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as u64).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // UINT/FIXED 64
8 => (
"bool".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"false".to_string(),
), // TYPE_BOOL
11 | 12 => (
"&'a [u8]".to_string(),
"Ok(bytes)".to_string(),
"&[]".to_string(),
), // MESSAGE/BYTES
_ => (
"&'a [u8]".to_string(),
"Ok(bytes)".to_string(),
"&[]".to_string(),
),
}
}
fn write_enum(enum_proto: &EnumDescriptorProto, output: &mut String) {
let enum_name = to_pascal_case(enum_proto.name().unwrap()); let enum_name = to_pascal_case(enum_proto.name().unwrap());
output.push_str(&format!( output.push_str(&format!(
@@ -117,10 +22,10 @@ fn write_enum(enum_proto: &EnumDescriptorProto, output: &mut String) {
let accessor = let accessor =
ProtoAccessor::new(val_data).expect("Failed to parse EnumValueDescriptorProto"); ProtoAccessor::new(val_data).expect("Failed to parse EnumValueDescriptorProto");
let (name_bytes, _) = accessor.get_value(1).expect("Enum value name missing"); let (name_bytes, _) = accessor.get_value(1).expect("Enum value name missing");
let name = str::from_utf8(name_bytes).expect("Enum value name invalid utf8"); let name = std::str::from_utf8(name_bytes).expect("Enum value name invalid utf8");
let (num_bytes, _) = accessor.get_value(2).expect("Enum value number missing"); let (num_bytes, _) = accessor.get_value(2).expect("Enum value number missing");
let (num, _) = let (num, _) =
roto_runtime::read_varint(num_bytes).expect("Enum value number invalid varint"); crate::runtime::read_varint(num_bytes).expect("Enum value number invalid varint");
let pascal_name = to_pascal_case(name); let pascal_name = to_pascal_case(name);
if num == 0 { if num == 0 {
@@ -147,10 +52,10 @@ fn write_enum(enum_proto: &EnumDescriptorProto, output: &mut String) {
let accessor = let accessor =
ProtoAccessor::new(val_data).expect("Failed to parse EnumValueDescriptorProto"); ProtoAccessor::new(val_data).expect("Failed to parse EnumValueDescriptorProto");
let (name_bytes, _) = accessor.get_value(1).expect("Enum value name missing"); let (name_bytes, _) = accessor.get_value(1).expect("Enum value name missing");
let name = str::from_utf8(name_bytes).expect("Enum value name invalid utf8"); let name = std::str::from_utf8(name_bytes).expect("Enum value name invalid utf8");
let (num_bytes, _) = accessor.get_value(2).expect("Enum value number missing"); let (num_bytes, _) = accessor.get_value(2).expect("Enum value number missing");
let (num, _) = let (num, _) =
roto_runtime::read_varint(num_bytes).expect("Enum value number invalid varint"); crate::runtime::read_varint(num_bytes).expect("Enum value number invalid varint");
output.push_str(&format!( output.push_str(&format!(
" {} => {}::{},\n", " {} => {}::{},\n",
@@ -167,9 +72,9 @@ fn write_enum(enum_proto: &EnumDescriptorProto, output: &mut String) {
)); ));
output.push_str(" }\n }\n}\n\n"); output.push_str(" }\n }\n}\n\n");
} }
pub fn write_message(msg_proto: &DescriptorProto, output: &mut String) {
fn write_message(msg_proto: &DescriptorProto, output: &mut String) {
let msg_name = to_pascal_case(msg_proto.name().unwrap()); let msg_name = to_pascal_case(msg_proto.name().unwrap());
let mod_name = to_snake_case(msg_proto.name().unwrap());
let mut fields_info = Vec::new(); let mut fields_info = Vec::new();
for field_res in msg_proto.field() { for field_res in msg_proto.field() {
@@ -179,7 +84,7 @@ fn write_message(msg_proto: &DescriptorProto, output: &mut String) {
let field_name = field_proto.name().unwrap(); let field_name = field_proto.name().unwrap();
let tag = field_proto.number().unwrap(); let tag = field_proto.number().unwrap();
let f_type = field_proto.r#type().unwrap() as i32; let f_type = field_proto.rype().unwrap() as i32;
let f_label = field_proto.label().unwrap() as i32; let f_label = field_proto.label().unwrap() as i32;
let oneof_index = field_proto.oneof_index().ok(); let oneof_index = field_proto.oneof_index().ok();
let is_map = field_proto let is_map = field_proto
@@ -332,10 +237,12 @@ fn write_message(msg_proto: &DescriptorProto, output: &mut String) {
let pascal_oneof_name = to_pascal_case(oneof_name); let pascal_oneof_name = to_pascal_case(oneof_name);
let snake_oneof_name = to_snake_case(oneof_name); let snake_oneof_name = to_snake_case(oneof_name);
output.push_str(&format!( let return_type = format!("{}::{}<'a>", mod_name, pascal_oneof_name);
" pub fn which_{}(&self) -> roto_runtime::Result<Option<{}::{}<'a>>> {{\n", let signature = format!(
snake_oneof_name, msg_name, pascal_oneof_name " pub fn which_{}(&self) -> roto_runtime::Result<Option<{}> > {{\n",
)); snake_oneof_name, return_type
);
output.push_str(&signature);
for (field_name, _tag, _f_type, _f_label, f_oneof_index, _is_map) in &fields_info { for (field_name, _tag, _f_type, _f_label, f_oneof_index, _is_map) in &fields_info {
if *f_oneof_index == Some(oneof_index as i32) { if *f_oneof_index == Some(oneof_index as i32) {
let safe_field_name = if field_name == "type" { let safe_field_name = if field_name == "type" {
@@ -348,8 +255,8 @@ fn write_message(msg_proto: &DescriptorProto, output: &mut String) {
field_name field_name
)); ));
output.push_str(&format!( output.push_str(&format!(
" return Ok(Some({}::{} (self.{}()?)));\n", " return Ok(Some({}::{}::{} (self.{}()?)));\n",
pascal_oneof_name, safe_field_name, safe_field_name mod_name, pascal_oneof_name, safe_field_name, safe_field_name
)); ));
output.push_str(" }\n"); output.push_str(" }\n");
} }
@@ -377,7 +284,7 @@ fn write_message(msg_proto: &DescriptorProto, output: &mut String) {
field_name.clone() field_name.clone()
}; };
let tag = field_proto.number().unwrap(); let tag = field_proto.number().unwrap();
let f_type = field_proto.r#type().unwrap() as i32; let f_type = field_proto.rype().unwrap() as i32;
let (rust_type, method) = map_type_to_rust_builder(f_type); let (rust_type, method) = map_type_to_rust_builder(f_type);
builder_fields.push((field_name, safe_name, tag as u32, rust_type, method)); builder_fields.push((field_name, safe_name, tag as u32, rust_type, method));
} }
@@ -414,7 +321,7 @@ fn write_message(msg_proto: &DescriptorProto, output: &mut String) {
" pub fn with(mut self, msg: &{}<'_>) -> roto_runtime::Result<Self> {{\n", " pub fn with(mut self, msg: &{}<'_>) -> roto_runtime::Result<Self> {{\n",
msg_name msg_name
)); ));
output.push_str(" for item in msg.raw_fields() {\n"); output.push_str(" for item in msg.accessor.raw_fields() {\n");
output.push_str(" let (field_number, raw_bytes) = item?;\n"); output.push_str(" let (field_number, raw_bytes) = item?;\n");
output.push_str(" let is_written = match field_number {\n"); output.push_str(" let is_written = match field_number {\n");
for (field_name, _, tag, _, _) in &builder_fields { for (field_name, _, tag, _, _) in &builder_fields {
@@ -434,6 +341,38 @@ 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 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!(" type Reader<'a> = {}<'a>;\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) -> roto_runtime::Result<Self> {\n");
output.push_str(&format!(" Ok(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(); let mut nested_enums = Vec::new();
for e_res in msg_proto.enum_type() { for e_res in msg_proto.enum_type() {
if let Ok((e, _)) = e_res { if let Ok((e, _)) = e_res {
@@ -488,60 +427,24 @@ fn write_message(msg_proto: &DescriptorProto, output: &mut String) {
output.push_str("}\n\n"); output.push_str("}\n\n");
} }
} }
pub fn generate_protobuf_code(
fn map_type_to_rust_builder(field_type: i32) -> (String, String) {
match field_type {
9 => ("&str".to_string(), "write_string".to_string()),
5 | 17 => ("i32".to_string(), "write_int32".to_string()),
3 | 4 | 8 | 13 | 14 | 18 => ("u64".to_string(), "write_varint".to_string()),
7 | 15 => ("u32".to_string(), "write_fixed32".to_string()),
6 | 16 => ("u64".to_string(), "write_fixed64".to_string()),
11 | 12 => ("&[u8]".to_string(), "write_bytes".to_string()),
_ => ("&[u8]".to_string(), "write_bytes".to_string()),
}
}
pub fn generate_rust_code(
set: &FileDescriptorSet, set: &FileDescriptorSet,
files_to_generate: Option<&[String]>, files_to_generate: Option<&[String]>,
generate_mod_files: bool, generate_mod_files: bool,
) -> Vec<(String, String)> { ) -> Vec<(String, String)> {
let mut generated_files = Vec::new(); generate_files_common(
set,
for file_res in set.file() { files_to_generate,
let (file_data, _) = file_res.expect("Failed to iterate file"); generate_mod_files,
let file_proto = DATA_IMPORTS,
FileDescriptorProto::new(file_data).expect("Failed to parse FileDescriptorProto"); |file_proto, output| {
let proto_name = file_proto.name().expect("File proto name missing");
if let Some(filter) = files_to_generate {
if !filter.contains(&proto_name.to_string()) {
continue;
}
}
let rust_file_name = format!("{}.rs", proto_name.replace(".proto", ""));
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("use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator};\n");
output.push_str("use std::str;\n\n");
for dep_res in file_proto.dependency() {
let (dep_data, _) = dep_res.expect("Failed to iterate dependency");
let dep_name = str::from_utf8(dep_data).expect("Dependency name invalid utf8");
let dep_mod_path = dep_name.replace(".proto", "").replace('/', "::");
output.push_str(&format!("use crate::{};\n", dep_mod_path));
}
output.push_str("\n");
// Enums // Enums
for enum_res in file_proto.enum_type() { for enum_res in file_proto.enum_type() {
let (enum_data, _) = enum_res.expect("Failed to iterate enum"); let (enum_data, _) = enum_res.expect("Failed to iterate enum");
write_enum( write_enum(
&EnumDescriptorProto::new(enum_data).expect("Failed to parse EnumDescriptorProto"), &EnumDescriptorProto::new(enum_data)
&mut output, .expect("Failed to parse EnumDescriptorProto"),
output,
); );
} }
@@ -550,61 +453,15 @@ pub fn generate_rust_code(
let (msg_data, _) = msg_res.expect("Failed to iterate message"); let (msg_data, _) = msg_res.expect("Failed to iterate message");
write_message( write_message(
&DescriptorProto::new(msg_data).expect("Failed to parse DescriptorProto"), &DescriptorProto::new(msg_data).expect("Failed to parse DescriptorProto"),
&mut output, output,
); );
} }
generated_files.push((rust_file_name, output)); },
)
} }
if !generate_mod_files {
return generated_files;
}
let mut all_paths: Vec<String> = generated_files.iter().map(|(p, _)| p.clone()).collect();
all_paths.sort();
let mut mod_files: HashMap<String, HashSet<String>> = HashMap::new();
for path in &all_paths {
let parts: Vec<&str> = path.split('/').collect();
let mut current_dir = String::new();
for i in 0..parts.len() - 1 {
if !current_dir.is_empty() {
current_dir.push('/');
}
current_dir.push_str(parts[i]);
let mod_path = format!("{}/mod.rs", current_dir);
let sub_mod = parts[i + 1].replace(".rs", "");
mod_files.entry(mod_path).or_default().insert(sub_mod);
}
}
let mut root_mods = HashSet::new();
for path in &all_paths {
let parts: Vec<&str> = path.split('/').collect();
root_mods.insert(parts[0].replace(".rs", ""));
}
let mut root_mod_content = String::new();
root_mod_content.push_str("// @generated by protoc-gen-roto — do not edit\n");
root_mod_content.push_str("#![allow(unused_imports)]\n\n");
let mut sorted_root_mods: Vec<_> = root_mods.into_iter().collect();
sorted_root_mods.sort();
for m in sorted_root_mods {
root_mod_content.push_str(&format!("pub mod {};\n", m));
}
generated_files.push(("mod.rs".to_string(), root_mod_content));
for (mod_path, sub_mods) in mod_files {
let mut content = String::new();
content.push_str("// @generated by protoc-gen-roto — do not edit\n");
content.push_str("#![allow(unused_imports)]\n\n");
let mut sorted_subs: Vec<_> = sub_mods.into_iter().collect();
sorted_subs.sort();
for sub in sorted_subs {
content.push_str(&format!("pub mod {};\n", sub));
}
generated_files.push((mod_path, content));
}
generated_files
}
File diff suppressed because it is too large Load Diff
+258
View File
@@ -0,0 +1,258 @@
use crate::google::protobuf::descriptor::{ServiceDescriptorProto, MethodDescriptorProto};
use crate::google::protobuf::descriptor::FileDescriptorSet;
use crate::generator::generate_files_common;
use crate::generator::SERVICE_IMPORTS;
use crate::generator::utils::{to_pascal_case, to_snake_case};
pub fn generate_service_code(
set: &FileDescriptorSet,
files_to_generate: Option<&[String]>,
generate_mod_files: bool,
) -> Vec<(String, String)> {
generate_files_common(
set,
files_to_generate,
generate_mod_files,
"",
|file_proto, output| {
let package = file_proto.package().unwrap_or("").to_string();
// 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"),
&package,
output,
);
}
},
)
}
pub fn write_service(svc_proto: &ServiceDescriptorProto, package: &str, output: &mut String) {
output.push_str(SERVICE_IMPORTS);
output.push_str("\n");
let svc_name = to_pascal_case(svc_proto.name().unwrap());
output.push_str(&format!(
"#[async_trait::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<tonic::Streaming<{}>>", input_owned)
} else {
format!("Request<{}>", input_owned)
};
let resp_type = if server_streaming {
format!(
"Response<Pin<Box<dyn Stream<Item = std::result::Result<{}, Status>> + Send>>>",
output_owned
)
} else {
format!("Response<{}>", output_owned)
};
output.push_str(&format!(
" async fn {}(&self, request: {}) -> std::result::Result<{}, Status>;\n",
method_name, req_type, resp_type
));
}
output.push_str("}\n\n");
let server_name = format!("{}Server", svc_name);
output.push_str(&format!(
"#[derive(Clone)]\npub struct {} {{\n",
server_name
));
output.push_str(&format!(" inner: Arc<dyn {}>,\n", svc_name));
output.push_str(" pool: Arc<BufferPool>,\n");
output.push_str("}\n\n");
output.push_str(&format!("impl {} {{\n", server_name));
output.push_str(&format!(
" pub fn new(inner: Arc<dyn {}>, pool: Arc<BufferPool>) -> Self {{\n",
svc_name
));
output.push_str(" Self { inner, pool }\n");
output.push_str(" }\n");
output.push_str("}\n\n");
output.push_str(&format!(
"impl tonic::server::NamedService for {} {{\n",
server_name
));
let full_svc_name = if package.is_empty() {
svc_proto.name().unwrap().to_string()
} else {
format!("{}.{}", package, svc_proto.name().unwrap())
};
output.push_str(&format!(
" const NAME: &'static str = \"{}\";\n",
full_svc_name
));
output.push_str("}\n\n");
output.push_str(&format!(
"impl Service<http::Request<BoxBody>> for {} {{\n",
server_name
));
output.push_str(" type Response = http::Response<BoxBody>;\n");
output.push_str(" type Error = std::convert::Infallible;\n");
output.push_str(" type Future = Pin<Box<dyn Future<Output = std::result::Result<Self::Response, Self::Error>> + Send>>;\n\n");
output.push_str(" fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<std::result::Result<(), Self::Error>> {\n");
output.push_str(" Poll::Ready(Ok(()))\n");
output.push_str(" }\n\n");
output.push_str(" fn call(&mut self, req: http::Request<BoxBody>) -> Self::Future {\n");
output.push_str(" let inner = self.inner.clone();\n");
output.push_str(" let pool = self.pool.clone();\n");
output.push_str(" Box::pin(async move {\n");
output.push_str(" let path = req.uri().path().to_string();\n");
output.push_str(" let body = req.into_body();\n");
output.push_str(" let mut buf = pool.get();\n");
output.push_str(" let mut stream = body;\n");
output.push_str(" while let Some(frame_result) = stream.frame().await {\n");
output.push_str(" let frame = frame_result.expect(\"Body frame error\");\n");
output.push_str(" if let Some(data) = frame.data_ref() {\n");
output.push_str(" buf.put(data.clone());\n");
output.push_str(" }\n");
output.push_str(" }\n\n");
output.push_str(" let total_len = buf.len();\n");
output.push_str(" let bytes_vec = buf.split_to(total_len).freeze();\n");
output.push_str(" pool.put(buf);\n");
output.push_str(" if bytes_vec.len() < 5 {\n");
output.push_str(" let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));\n");
output.push_str(" return Ok(http::Response::builder().status(200).body(res_body).unwrap());\n");
output.push_str(" }\n\n");
output.push_str(" let payload = bytes_vec.slice(5..);\n");
output.push_str(" let mut routed = false;\n\n");
let mut methods = Vec::new();
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 original_method_name = method_proto.name().unwrap().to_string();
let method_name = to_snake_case(&original_method_name);
let input_full_name = method_proto.input_type().unwrap();
let input_type = input_full_name.split('.').last().unwrap();
let input_owned = format!("Owned{}", input_type);
let server_streaming = method_proto.server_streaming().unwrap_or(false);
methods.push((
original_method_name,
method_name,
input_owned,
server_streaming,
));
}
for (original_method_name, method_name, input_owned, server_streaming) in methods {
if server_streaming {
// For streaming RPCs, we don't implement the server logic yet.
// We just make it compile by returning a "not implemented" response.
let full_path = if package.is_empty() {
format!("/{}/{}", svc_proto.name().unwrap(), original_method_name)
} else {
format!(
"/{}.{}/{}",
package,
svc_proto.name().unwrap(),
original_method_name
)
};
output.push_str(&format!(" if path == \"{}\" {{\n", full_path));
output.push_str(" let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));\n");
output.push_str(" return Ok(http::Response::builder().status(200).body(res_body).unwrap());\n");
output.push_str(" }\n");
continue;
}
let full_path = if package.is_empty() {
format!("/{}/{}", svc_proto.name().unwrap(), original_method_name)
} else {
format!(
"/{}.{}/{}",
package,
svc_proto.name().unwrap(),
original_method_name
)
};
output.push_str(&format!(" if path == \"{}\" {{\n", full_path));
output.push_str(&format!(
" let request_msg = match {}::decode(payload) {{\n",
input_owned
));
output.push_str(" Ok(msg) => msg,\n");
output.push_str(" Err(e) => {\n");
output.push_str(" let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));\n");
output.push_str(" return Ok(http::Response::builder().status(200).body(res_body).unwrap());\n");
output.push_str(" }\n");
output.push_str(" };\n\n");
output.push_str(&format!(
" let response = match inner.{}(Request::new(request_msg)).await {{\n",
method_name
));
output.push_str(" Ok(res) => res,\n");
output.push_str(" Err(e) => {\n");
output.push_str(" let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));\n");
output.push_str(" return Ok(http::Response::builder().status(200).body(res_body).unwrap());\n");
output.push_str(" }\n");
output.push_str(" };\n\n");
output.push_str(" let response_msg = response.into_inner();\n");
output.push_str(" let response_bytes = response_msg.bytes();\n");
output.push_str(" let mut res_buf = pool.get();\n");
output.push_str(" res_buf.put_u8(0);\n");
output.push_str(" let len = response_bytes.len() as u32;\n");
output.push_str(" res_buf.put_slice(&len.to_be_bytes());\n");
output.push_str(" res_buf.put_slice(&response_bytes);\n");
output.push_str(" let frame_len = res_buf.len();\n");
output.push_str(" let frame = res_buf.split_to(frame_len).freeze();\n");
output.push_str(" pool.put(res_buf);\n");
output.push_str(
" let res_body = BoxBody::new(StatusBody::new(Some(frame), 0));\n",
);
output.push_str(" routed = true;\n");
output.push_str(" return Ok(http::Response::builder().status(200).header(\"content-type\", \"application/grpc\").body(res_body).unwrap());\n");
output.push_str(" }\n");
}
output.push_str(" if !routed {\n");
output.push_str(" let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));\n");
output.push_str(" return Ok(http::Response::builder().status(200).body(res_body).unwrap());\n");
output.push_str(" }\n");
output.push_str(" Ok(http::Response::builder().status(200).body(BoxBody::new(StatusBody::new(None, 0))).unwrap())\n");
output.push_str(" })\n");
output.push_str(" }\n");
output.push_str("}\n");
}
+151
View File
@@ -0,0 +1,151 @@
use crate::google::protobuf::descriptor::FieldDescriptorProto;
use crate::google::protobuf::descriptor::DescriptorProto;
pub fn map_type_to_rust_accessor(
field_type: i32,
label: i32,
is_map: bool,
) -> (String, String, String) {
if label == 3 {
// LABEL_REPEATED
let iterator_type = if is_map {
"roto_runtime::MapFieldIterator<'a>"
} else {
"roto_runtime::RepeatedFieldIterator<'a>"
};
return (
iterator_type.to_string(),
"".to_string(), // Not used for repeated fields in the same way
"".to_string(), // Not used for repeated fields
);
}
match field_type {
9 => (
"&'a str".to_string(),
"core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"\"\"".to_string(),
), // TYPE_STRING
1 => (
"f64".to_string(),
"Ok(f64::from_le_bytes(bytes.try_into().map_err(|_| roto_runtime::RotoError::WireFormatViolation)?))".to_string(),
"0.0".to_string(),
), // TYPE_DOUBLE
2 => (
"f32".to_string(),
"Ok(f32::from_le_bytes(bytes.try_into().map_err(|_| roto_runtime::RotoError::WireFormatViolation)?))".to_string(),
"0.0".to_string(),
), // TYPE_FLOAT
3 | 5 | 15 | 17 => (
"i32".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // INT/SINT/SFIXED 32
4 | 6 | 13 => (
"u32".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as u32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // UINT/FIXED 32
16 | 18 => (
"i64".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as i64).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // SINT/SFIXED 64
7 | 14 => (
"u64".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as u64).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // UINT/FIXED 64
8 => (
"bool".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"false".to_string(),
), // TYPE_BOOL
11 | 12 => (
"&'a [u8]".to_string(),
"Ok(bytes)".to_string(),
"&[]".to_string(),
), // MESSAGE/BYTES
_ => (
"&'a [u8]".to_string(),
"Ok(bytes)".to_string(),
"&[]".to_string(),
),
}
}
EOF > /opt/workspace/codegen/src/generator/types.rs
use crate::google::protobuf::descriptor::FieldDescriptorProto;
use crate::google::protobuf::descriptor::DescriptorProto;
pub fn map_type_to_rust_accessor(
field_type: i32,
label: i32,
is_map: bool,
) -> (String, String, String) {
if label == 3 {
// LABEL_REPEATED
let iterator_type = if is_map {
"roto_runtime::MapFieldIterator<'a>"
} else {
"roto_runtime::RepeatedFieldIterator<'a>"
};
return (
iterator_type.to_string(),
"".to_string(), // Not used for repeated fields in the same way
"".to_string(), // Not used for repeated fields
);
}
match field_type {
9 => (
"&'a str".to_string(),
"core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"\"\"".to_string(),
), // TYPE_STRING
1 => (
"f64".to_string(),
"Ok(f64::from_le_bytes(bytes.try_into().map_err(|_| roto_runtime::RotoError::WireFormatViolation)?))".to_string(),
"0.0".to_string(),
), // TYPE_DOUBLE
2 => (
"f32".to_string(),
"Ok(f32::from_le_bytes(bytes.try_into().map_err(|_| roto_runtime::RotoError::WireFormatViolation)?))".to_string(),
"0.0".to_string(),
), // TYPE_FLOAT
3 | 5 | 15 | 17 => (
"i32".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // INT/SINT/SFIXED 32
4 | 6 | 13 => (
"u32".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as u32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // UINT/FIXED 32
16 | 18 => (
"i64".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as i64).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // SINT/SFIXED 64
7 | 14 => (
"u64".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v as u64).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"0".to_string(),
), // UINT/FIXED 64
8 => (
"bool".to_string(),
"roto_runtime::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
"false".to_string(),
), // TYPE_BOOL
11 | 12 => (
"&'a [u8]".to_string(),
"Ok(bytes)".to_string(),
"&[]".to_string(),
), // MESSAGE/BYTES
_ => (
"&'a [u8]".to_string(),
"Ok(bytes)".to_string(),
"&[]".to_string(),
),
}
}
+32
View File
@@ -0,0 +1,32 @@
pub const DATA_IMPORTS: &str = "use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};\nuse core::str;\nuse bytes::{Bytes, BytesMut, Buf, BufMut};\n";
pub fn to_pascal_case(s: &str) -> String {
s.split('_')
.map(|word| {
let mut chars = word.chars();
match chars.next() {
None => String::new(),
Some(f) => {
let first = f.to_uppercase().collect::<String>();
let rest = chars.as_str().to_lowercase();
first + &rest
}
}
})
.collect()
}
pub fn to_snake_case(s: &str) -> String {
let mut result = String::new();
for (i, c) in s.chars().enumerate() {
if c.is_uppercase() {
if i > 0 {
result.push('_');
}
result.push(c.to_ascii_lowercase());
} else {
result.push(c);
}
}
result
}
+311 -209
View File
@@ -1,15 +1,26 @@
// @generated by protoc-gen-roto — do not edit // @generated by protoc-gen-roto — do not edit
#![allow(unused_imports)] #[allow(unused_imports)]
use roto_runtime::{ use crate::runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator};
ProtoAccessor, ProtoBuilder, RepeatedFieldIterator, Result, RotoError, read_varint,
};
use std::str; use std::str;
use bytes::{Bytes, BytesMut, Buf, BufMut};
use tonic::{Request, Response, Status};
use tokio_stream::Stream;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use std::future::Future;
use tonic::body::BoxBody;
use tower::Service;
use futures_util::StreamExt;
use http_body_util::BodyExt;
use http_body::Body;
use roto_tonic::{BufferPool, StatusBody};
use crate::google::protobuf::descriptor; use crate::google::protobuf::descriptor;
pub struct Version<'a> { pub struct Version<'a> {
accessor: roto_runtime::ProtoAccessor<'a>, accessor: crate::runtime::ProtoAccessor<'a>,
major_offset: Option<usize>, major_offset: Option<usize>,
minor_offset: Option<usize>, minor_offset: Option<usize>,
patch_offset: Option<usize>, patch_offset: Option<usize>,
@@ -17,26 +28,18 @@ pub struct Version<'a> {
} }
impl<'a> Version<'a> { impl<'a> Version<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> { pub fn new(data: &'a [u8]) -> crate::runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?; let accessor = crate::runtime::ProtoAccessor::new(data)?;
let mut major_offset = None; let mut major_offset = None;
let mut minor_offset = None; let mut minor_offset = None;
let mut patch_offset = None; let mut patch_offset = None;
let mut suffix_offset = None; let mut suffix_offset = None;
for item in accessor.fields() { for item in accessor.fields() {
let (offset, tag, _) = item?; let (offset, tag, _) = item?;
if tag.field_number == 1 { if tag.field_number == 1 { major_offset = Some(offset); }
major_offset = Some(offset); if tag.field_number == 2 { minor_offset = Some(offset); }
} if tag.field_number == 3 { patch_offset = Some(offset); }
if tag.field_number == 2 { if tag.field_number == 4 { suffix_offset = Some(offset); }
minor_offset = Some(offset);
}
if tag.field_number == 3 {
patch_offset = Some(offset);
}
if tag.field_number == 4 {
suffix_offset = Some(offset);
}
} }
Ok(Self { Ok(Self {
@@ -48,51 +51,62 @@ impl<'a> Version<'a> {
}) })
} }
pub fn major(&self) -> roto_runtime::Result<i32> { pub fn major(&self) -> crate::runtime::Result<i32> {
let offset = self let offset = self.major_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
.major_offset
.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes) crate::runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
.map(|(v, _)| v as i32)
.map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn minor(&self) -> roto_runtime::Result<i32> { pub fn major_or_default(&self) -> crate::runtime::Result<i32> {
let offset = self self.major().or(Ok(0))
.minor_offset
.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes)
.map(|(v, _)| v as i32)
.map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn patch(&self) -> roto_runtime::Result<i32> { pub fn has_major(&self) -> bool { self.major_offset.is_some() }
let offset = self
.patch_offset pub fn minor(&self) -> crate::runtime::Result<i32> {
.ok_or(roto_runtime::RotoError::FieldNotFound)?; let offset = self.minor_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes) crate::runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
.map(|(v, _)| v as i32)
.map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn suffix(&self) -> roto_runtime::Result<&'a str> { pub fn minor_or_default(&self) -> crate::runtime::Result<i32> {
let offset = self self.minor().or(Ok(0))
.suffix_offset
.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> { pub fn has_minor(&self) -> bool { self.minor_offset.is_some() }
pub fn patch(&self) -> crate::runtime::Result<i32> {
let offset = self.patch_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
}
pub fn patch_or_default(&self) -> crate::runtime::Result<i32> {
self.patch().or(Ok(0))
}
pub fn has_patch(&self) -> bool { self.patch_offset.is_some() }
pub fn suffix(&self) -> crate::runtime::Result<&'a str> {
let offset = self.suffix_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
}
pub fn suffix_or_default(&self) -> crate::runtime::Result<&'a str> {
self.suffix().or(Ok(""))
}
pub fn has_suffix(&self) -> bool { self.suffix_offset.is_some() }
pub fn raw_fields(&self) -> crate::runtime::RawFieldIterator<'a> {
self.accessor.raw_fields() self.accessor.raw_fields()
} }
} }
pub struct VersionBuilder<'b> { pub struct VersionBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>, builder: crate::runtime::ProtoBuilder<'b>,
major_written: bool, major_written: bool,
minor_written: bool, minor_written: bool,
patch_written: bool, patch_written: bool,
@@ -102,7 +116,7 @@ pub struct VersionBuilder<'b> {
impl<'b> VersionBuilder<'b> { impl<'b> VersionBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> VersionBuilder<'_> { pub fn builder(buf: &mut [u8]) -> VersionBuilder<'_> {
VersionBuilder { VersionBuilder {
builder: roto_runtime::ProtoBuilder::new(buf), builder: crate::runtime::ProtoBuilder::new(buf),
major_written: false, major_written: false,
minor_written: false, minor_written: false,
patch_written: false, patch_written: false,
@@ -110,31 +124,31 @@ impl<'b> VersionBuilder<'b> {
} }
} }
pub fn major(mut self, value: i32) -> roto_runtime::Result<Self> { pub fn major(mut self, value: i32) -> crate::runtime::Result<Self> {
self.builder.write_int32(1, value)?; self.builder.write_int32(1, value)?;
self.major_written = true; self.major_written = true;
Ok(self) Ok(self)
} }
pub fn minor(mut self, value: i32) -> roto_runtime::Result<Self> { pub fn minor(mut self, value: i32) -> crate::runtime::Result<Self> {
self.builder.write_int32(2, value)?; self.builder.write_int32(2, value)?;
self.minor_written = true; self.minor_written = true;
Ok(self) Ok(self)
} }
pub fn patch(mut self, value: i32) -> roto_runtime::Result<Self> { pub fn patch(mut self, value: i32) -> crate::runtime::Result<Self> {
self.builder.write_int32(3, value)?; self.builder.write_int32(3, value)?;
self.patch_written = true; self.patch_written = true;
Ok(self) Ok(self)
} }
pub fn suffix(mut self, value: &str) -> roto_runtime::Result<Self> { pub fn suffix(mut self, value: &str) -> crate::runtime::Result<Self> {
self.builder.write_string(4, value)?; self.builder.write_string(4, value)?;
self.suffix_written = true; self.suffix_written = true;
Ok(self) Ok(self)
} }
pub fn with(mut self, msg: &Version<'_>) -> roto_runtime::Result<Self> { pub fn with(mut self, msg: &Version<'_>) -> crate::runtime::Result<Self> {
for item in msg.raw_fields() { for item in msg.raw_fields() {
let (field_number, raw_bytes) = item?; let (field_number, raw_bytes) = item?;
let is_written = match field_number { let is_written = match field_number {
@@ -151,13 +165,34 @@ impl<'b> VersionBuilder<'b> {
Ok(self) Ok(self)
} }
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> { pub fn finish(self) -> crate::runtime::Result<&'b mut [u8]> {
self.builder.finish() self.builder.finish()
} }
} }
pub struct OwnedVersion {
pub data: bytes::Bytes,
}
impl crate::runtime::RotoOwned for OwnedVersion {
type Reader<'a> = Version<'a>;
fn reader(&self) -> Version<'_> {
Version::new(&self.data).expect("failed to create reader")
}
}
impl crate::runtime::RotoMessage for OwnedVersion {
fn decode(buf: bytes::Bytes) -> crate::runtime::Result<Self> {
Ok(OwnedVersion { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct CodeGeneratorRequest<'a> { pub struct CodeGeneratorRequest<'a> {
accessor: roto_runtime::ProtoAccessor<'a>, accessor: crate::runtime::ProtoAccessor<'a>,
file_to_generate_start: Option<usize>, file_to_generate_start: Option<usize>,
file_to_generate_end: Option<usize>, file_to_generate_end: Option<usize>,
parameter_offset: Option<usize>, parameter_offset: Option<usize>,
@@ -169,8 +204,8 @@ pub struct CodeGeneratorRequest<'a> {
} }
impl<'a> CodeGeneratorRequest<'a> { impl<'a> CodeGeneratorRequest<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> { pub fn new(data: &'a [u8]) -> crate::runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?; let accessor = crate::runtime::ProtoAccessor::new(data)?;
let mut file_to_generate_start = None; let mut file_to_generate_start = None;
let mut file_to_generate_end = None; let mut file_to_generate_end = None;
let mut parameter_offset = None; let mut parameter_offset = None;
@@ -182,91 +217,84 @@ impl<'a> CodeGeneratorRequest<'a> {
for item in accessor.fields() { for item in accessor.fields() {
let (offset, tag, _) = item?; let (offset, tag, _) = item?;
if tag.field_number == 1 { if tag.field_number == 1 {
if file_to_generate_start.is_none() { if file_to_generate_start.is_none() { file_to_generate_start = Some(offset); }
file_to_generate_start = Some(offset);
}
file_to_generate_end = Some(offset); file_to_generate_end = Some(offset);
} }
if tag.field_number == 2 { if tag.field_number == 2 { parameter_offset = Some(offset); }
parameter_offset = Some(offset);
}
if tag.field_number == 15 { if tag.field_number == 15 {
if proto_file_start.is_none() { if proto_file_start.is_none() { proto_file_start = Some(offset); }
proto_file_start = Some(offset);
}
proto_file_end = Some(offset); proto_file_end = Some(offset);
} }
if tag.field_number == 17 { if tag.field_number == 17 {
if source_file_descriptors_start.is_none() { if source_file_descriptors_start.is_none() { source_file_descriptors_start = Some(offset); }
source_file_descriptors_start = Some(offset);
}
source_file_descriptors_end = Some(offset); source_file_descriptors_end = Some(offset);
} }
if tag.field_number == 3 { if tag.field_number == 3 { compiler_version_offset = Some(offset); }
compiler_version_offset = Some(offset);
}
} }
Ok(Self { Ok(Self {
accessor, accessor,
file_to_generate_start, file_to_generate_start, file_to_generate_end,
file_to_generate_end,
parameter_offset, parameter_offset,
proto_file_start, proto_file_start, proto_file_end,
proto_file_end, source_file_descriptors_start, source_file_descriptors_end,
source_file_descriptors_start,
source_file_descriptors_end,
compiler_version_offset, compiler_version_offset,
}) })
} }
pub fn file_to_generate(&self) -> roto_runtime::RepeatedFieldIterator<'a> { pub fn file_to_generate(&self) -> crate::runtime::RepeatedFieldIterator<'a> {
match (self.file_to_generate_start, self.file_to_generate_end) { match (self.file_to_generate_start, self.file_to_generate_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(1, start, end), (Some(start), Some(end)) => self.accessor.iter_repeated_range(1, start, end),
_ => self.accessor.iter_repeated(1), _ => self.accessor.iter_repeated(1),
} }
} }
pub fn parameter(&self) -> roto_runtime::Result<&'a str> { pub fn parameter(&self) -> crate::runtime::Result<&'a str> {
let offset = self let offset = self.parameter_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
.parameter_offset
.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation) str::from_utf8(bytes).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
} }
pub fn proto_file(&self) -> roto_runtime::RepeatedFieldIterator<'a> { pub fn parameter_or_default(&self) -> crate::runtime::Result<&'a str> {
self.parameter().or(Ok(""))
}
pub fn has_parameter(&self) -> bool { self.parameter_offset.is_some() }
pub fn proto_file(&self) -> crate::runtime::RepeatedFieldIterator<'a> {
match (self.proto_file_start, self.proto_file_end) { match (self.proto_file_start, self.proto_file_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end), (Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end),
_ => self.accessor.iter_repeated(15), _ => self.accessor.iter_repeated(15),
} }
} }
pub fn source_file_descriptors(&self) -> roto_runtime::RepeatedFieldIterator<'a> { pub fn source_file_descriptors(&self) -> crate::runtime::RepeatedFieldIterator<'a> {
match ( match (self.source_file_descriptors_start, self.source_file_descriptors_end) {
self.source_file_descriptors_start,
self.source_file_descriptors_end,
) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(17, start, end), (Some(start), Some(end)) => self.accessor.iter_repeated_range(17, start, end),
_ => self.accessor.iter_repeated(17), _ => self.accessor.iter_repeated(17),
} }
} }
pub fn compiler_version(&self) -> roto_runtime::Result<&'a [u8]> { pub fn compiler_version(&self) -> crate::runtime::Result<&'a [u8]> {
let offset = self let offset = self.compiler_version_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
.compiler_version_offset
.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes) Ok(bytes)
} }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> { pub fn compiler_version_or_default(&self) -> crate::runtime::Result<&'a [u8]> {
self.compiler_version().or(Ok(&[]))
}
pub fn has_compiler_version(&self) -> bool { self.compiler_version_offset.is_some() }
pub fn raw_fields(&self) -> crate::runtime::RawFieldIterator<'a> {
self.accessor.raw_fields() self.accessor.raw_fields()
} }
} }
pub struct CodeGeneratorRequestBuilder<'b> { pub struct CodeGeneratorRequestBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>, builder: crate::runtime::ProtoBuilder<'b>,
file_to_generate_written: bool, file_to_generate_written: bool,
parameter_written: bool, parameter_written: bool,
proto_file_written: bool, proto_file_written: bool,
@@ -277,7 +305,7 @@ pub struct CodeGeneratorRequestBuilder<'b> {
impl<'b> CodeGeneratorRequestBuilder<'b> { impl<'b> CodeGeneratorRequestBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> CodeGeneratorRequestBuilder<'_> { pub fn builder(buf: &mut [u8]) -> CodeGeneratorRequestBuilder<'_> {
CodeGeneratorRequestBuilder { CodeGeneratorRequestBuilder {
builder: roto_runtime::ProtoBuilder::new(buf), builder: crate::runtime::ProtoBuilder::new(buf),
file_to_generate_written: false, file_to_generate_written: false,
parameter_written: false, parameter_written: false,
proto_file_written: false, proto_file_written: false,
@@ -286,37 +314,37 @@ impl<'b> CodeGeneratorRequestBuilder<'b> {
} }
} }
pub fn file_to_generate(mut self, value: &str) -> roto_runtime::Result<Self> { pub fn file_to_generate(mut self, value: &str) -> crate::runtime::Result<Self> {
self.builder.write_string(1, value)?; self.builder.write_string(1, value)?;
self.file_to_generate_written = true; self.file_to_generate_written = true;
Ok(self) Ok(self)
} }
pub fn parameter(mut self, value: &str) -> roto_runtime::Result<Self> { pub fn parameter(mut self, value: &str) -> crate::runtime::Result<Self> {
self.builder.write_string(2, value)?; self.builder.write_string(2, value)?;
self.parameter_written = true; self.parameter_written = true;
Ok(self) Ok(self)
} }
pub fn proto_file(mut self, value: &[u8]) -> roto_runtime::Result<Self> { pub fn proto_file(mut self, value: &[u8]) -> crate::runtime::Result<Self> {
self.builder.write_bytes(15, value)?; self.builder.write_bytes(15, value)?;
self.proto_file_written = true; self.proto_file_written = true;
Ok(self) Ok(self)
} }
pub fn source_file_descriptors(mut self, value: &[u8]) -> roto_runtime::Result<Self> { pub fn source_file_descriptors(mut self, value: &[u8]) -> crate::runtime::Result<Self> {
self.builder.write_bytes(17, value)?; self.builder.write_bytes(17, value)?;
self.source_file_descriptors_written = true; self.source_file_descriptors_written = true;
Ok(self) Ok(self)
} }
pub fn compiler_version(mut self, value: &[u8]) -> roto_runtime::Result<Self> { pub fn compiler_version(mut self, value: &[u8]) -> crate::runtime::Result<Self> {
self.builder.write_bytes(3, value)?; self.builder.write_bytes(3, value)?;
self.compiler_version_written = true; self.compiler_version_written = true;
Ok(self) Ok(self)
} }
pub fn with(mut self, msg: &CodeGeneratorRequest<'_>) -> roto_runtime::Result<Self> { pub fn with(mut self, msg: &CodeGeneratorRequest<'_>) -> crate::runtime::Result<Self> {
for item in msg.raw_fields() { for item in msg.raw_fields() {
let (field_number, raw_bytes) = item?; let (field_number, raw_bytes) = item?;
let is_written = match field_number { let is_written = match field_number {
@@ -334,13 +362,34 @@ impl<'b> CodeGeneratorRequestBuilder<'b> {
Ok(self) Ok(self)
} }
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> { pub fn finish(self) -> crate::runtime::Result<&'b mut [u8]> {
self.builder.finish() self.builder.finish()
} }
} }
pub struct OwnedCodeGeneratorRequest {
pub data: bytes::Bytes,
}
impl crate::runtime::RotoOwned for OwnedCodeGeneratorRequest {
type Reader<'a> = CodeGeneratorRequest<'a>;
fn reader(&self) -> CodeGeneratorRequest<'_> {
CodeGeneratorRequest::new(&self.data).expect("failed to create reader")
}
}
impl crate::runtime::RotoMessage for OwnedCodeGeneratorRequest {
fn decode(buf: bytes::Bytes) -> crate::runtime::Result<Self> {
Ok(OwnedCodeGeneratorRequest { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct CodeGeneratorResponse<'a> { pub struct CodeGeneratorResponse<'a> {
accessor: roto_runtime::ProtoAccessor<'a>, accessor: crate::runtime::ProtoAccessor<'a>,
error_offset: Option<usize>, error_offset: Option<usize>,
supported_features_offset: Option<usize>, supported_features_offset: Option<usize>,
minimum_edition_offset: Option<usize>, minimum_edition_offset: Option<usize>,
@@ -350,8 +399,8 @@ pub struct CodeGeneratorResponse<'a> {
} }
impl<'a> CodeGeneratorResponse<'a> { impl<'a> CodeGeneratorResponse<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> { pub fn new(data: &'a [u8]) -> crate::runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?; let accessor = crate::runtime::ProtoAccessor::new(data)?;
let mut error_offset = None; let mut error_offset = None;
let mut supported_features_offset = None; let mut supported_features_offset = None;
let mut minimum_edition_offset = None; let mut minimum_edition_offset = None;
@@ -360,22 +409,12 @@ impl<'a> CodeGeneratorResponse<'a> {
let mut file_end = None; let mut file_end = None;
for item in accessor.fields() { for item in accessor.fields() {
let (offset, tag, _) = item?; let (offset, tag, _) = item?;
if tag.field_number == 1 { if tag.field_number == 1 { error_offset = Some(offset); }
error_offset = Some(offset); if tag.field_number == 2 { supported_features_offset = Some(offset); }
} if tag.field_number == 3 { minimum_edition_offset = Some(offset); }
if tag.field_number == 2 { if tag.field_number == 4 { maximum_edition_offset = Some(offset); }
supported_features_offset = Some(offset);
}
if tag.field_number == 3 {
minimum_edition_offset = Some(offset);
}
if tag.field_number == 4 {
maximum_edition_offset = Some(offset);
}
if tag.field_number == 15 { if tag.field_number == 15 {
if file_start.is_none() { if file_start.is_none() { file_start = Some(offset); }
file_start = Some(offset);
}
file_end = Some(offset); file_end = Some(offset);
} }
} }
@@ -386,63 +425,73 @@ impl<'a> CodeGeneratorResponse<'a> {
supported_features_offset, supported_features_offset,
minimum_edition_offset, minimum_edition_offset,
maximum_edition_offset, maximum_edition_offset,
file_start, file_start, file_end,
file_end,
}) })
} }
pub fn error(&self) -> roto_runtime::Result<&'a str> { pub fn error(&self) -> crate::runtime::Result<&'a str> {
let offset = self let offset = self.error_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
.error_offset
.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation) str::from_utf8(bytes).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
} }
pub fn supported_features(&self) -> roto_runtime::Result<u32> { pub fn error_or_default(&self) -> crate::runtime::Result<&'a str> {
let offset = self self.error().or(Ok(""))
.supported_features_offset
.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes)
.map(|(v, _)| v as u32)
.map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn minimum_edition(&self) -> roto_runtime::Result<i32> { pub fn has_error(&self) -> bool { self.error_offset.is_some() }
let offset = self
.minimum_edition_offset pub fn supported_features(&self) -> crate::runtime::Result<u32> {
.ok_or(roto_runtime::RotoError::FieldNotFound)?; let offset = self.supported_features_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes) crate::runtime::read_varint(bytes).map(|(v, _)| v as u32).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
.map(|(v, _)| v as i32)
.map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn maximum_edition(&self) -> roto_runtime::Result<i32> { pub fn supported_features_or_default(&self) -> crate::runtime::Result<u32> {
let offset = self self.supported_features().or(Ok(0))
.maximum_edition_offset
.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes)
.map(|(v, _)| v as i32)
.map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn file(&self) -> roto_runtime::RepeatedFieldIterator<'a> { pub fn has_supported_features(&self) -> bool { self.supported_features_offset.is_some() }
pub fn minimum_edition(&self) -> crate::runtime::Result<i32> {
let offset = self.minimum_edition_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
}
pub fn minimum_edition_or_default(&self) -> crate::runtime::Result<i32> {
self.minimum_edition().or(Ok(0))
}
pub fn has_minimum_edition(&self) -> bool { self.minimum_edition_offset.is_some() }
pub fn maximum_edition(&self) -> crate::runtime::Result<i32> {
let offset = self.maximum_edition_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
}
pub fn maximum_edition_or_default(&self) -> crate::runtime::Result<i32> {
self.maximum_edition().or(Ok(0))
}
pub fn has_maximum_edition(&self) -> bool { self.maximum_edition_offset.is_some() }
pub fn file(&self) -> crate::runtime::RepeatedFieldIterator<'a> {
match (self.file_start, self.file_end) { match (self.file_start, self.file_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end), (Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end),
_ => self.accessor.iter_repeated(15), _ => self.accessor.iter_repeated(15),
} }
} }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> { pub fn raw_fields(&self) -> crate::runtime::RawFieldIterator<'a> {
self.accessor.raw_fields() self.accessor.raw_fields()
} }
} }
pub struct CodeGeneratorResponseBuilder<'b> { pub struct CodeGeneratorResponseBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>, builder: crate::runtime::ProtoBuilder<'b>,
error_written: bool, error_written: bool,
supported_features_written: bool, supported_features_written: bool,
minimum_edition_written: bool, minimum_edition_written: bool,
@@ -453,7 +502,7 @@ pub struct CodeGeneratorResponseBuilder<'b> {
impl<'b> CodeGeneratorResponseBuilder<'b> { impl<'b> CodeGeneratorResponseBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> CodeGeneratorResponseBuilder<'_> { pub fn builder(buf: &mut [u8]) -> CodeGeneratorResponseBuilder<'_> {
CodeGeneratorResponseBuilder { CodeGeneratorResponseBuilder {
builder: roto_runtime::ProtoBuilder::new(buf), builder: crate::runtime::ProtoBuilder::new(buf),
error_written: false, error_written: false,
supported_features_written: false, supported_features_written: false,
minimum_edition_written: false, minimum_edition_written: false,
@@ -462,37 +511,37 @@ impl<'b> CodeGeneratorResponseBuilder<'b> {
} }
} }
pub fn error(mut self, value: &str) -> roto_runtime::Result<Self> { pub fn error(mut self, value: &str) -> crate::runtime::Result<Self> {
self.builder.write_string(1, value)?; self.builder.write_string(1, value)?;
self.error_written = true; self.error_written = true;
Ok(self) Ok(self)
} }
pub fn supported_features(mut self, value: u64) -> roto_runtime::Result<Self> { pub fn supported_features(mut self, value: u64) -> crate::runtime::Result<Self> {
self.builder.write_varint(2, value)?; self.builder.write_varint(2, value)?;
self.supported_features_written = true; self.supported_features_written = true;
Ok(self) Ok(self)
} }
pub fn minimum_edition(mut self, value: i32) -> roto_runtime::Result<Self> { pub fn minimum_edition(mut self, value: i32) -> crate::runtime::Result<Self> {
self.builder.write_int32(3, value)?; self.builder.write_int32(3, value)?;
self.minimum_edition_written = true; self.minimum_edition_written = true;
Ok(self) Ok(self)
} }
pub fn maximum_edition(mut self, value: i32) -> roto_runtime::Result<Self> { pub fn maximum_edition(mut self, value: i32) -> crate::runtime::Result<Self> {
self.builder.write_int32(4, value)?; self.builder.write_int32(4, value)?;
self.maximum_edition_written = true; self.maximum_edition_written = true;
Ok(self) Ok(self)
} }
pub fn file(mut self, value: &[u8]) -> roto_runtime::Result<Self> { pub fn file(mut self, value: &[u8]) -> crate::runtime::Result<Self> {
self.builder.write_bytes(15, value)?; self.builder.write_bytes(15, value)?;
self.file_written = true; self.file_written = true;
Ok(self) Ok(self)
} }
pub fn with(mut self, msg: &CodeGeneratorResponse<'_>) -> roto_runtime::Result<Self> { pub fn with(mut self, msg: &CodeGeneratorResponse<'_>) -> crate::runtime::Result<Self> {
for item in msg.raw_fields() { for item in msg.raw_fields() {
let (field_number, raw_bytes) = item?; let (field_number, raw_bytes) = item?;
let is_written = match field_number { let is_written = match field_number {
@@ -510,11 +559,32 @@ impl<'b> CodeGeneratorResponseBuilder<'b> {
Ok(self) Ok(self)
} }
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> { pub fn finish(self) -> crate::runtime::Result<&'b mut [u8]> {
self.builder.finish() self.builder.finish()
} }
} }
pub struct OwnedCodeGeneratorResponse {
pub data: bytes::Bytes,
}
impl crate::runtime::RotoOwned for OwnedCodeGeneratorResponse {
type Reader<'a> = CodeGeneratorResponse<'a>;
fn reader(&self) -> CodeGeneratorResponse<'_> {
CodeGeneratorResponse::new(&self.data).expect("failed to create reader")
}
}
impl crate::runtime::RotoMessage for OwnedCodeGeneratorResponse {
fn decode(buf: bytes::Bytes) -> crate::runtime::Result<Self> {
Ok(OwnedCodeGeneratorResponse { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub mod code_generator_response { pub mod code_generator_response {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)] #[repr(i32)]
@@ -536,7 +606,7 @@ pub mod code_generator_response {
} }
pub struct File<'a> { pub struct File<'a> {
accessor: roto_runtime::ProtoAccessor<'a>, accessor: crate::runtime::ProtoAccessor<'a>,
name_offset: Option<usize>, name_offset: Option<usize>,
insertion_point_offset: Option<usize>, insertion_point_offset: Option<usize>,
content_offset: Option<usize>, content_offset: Option<usize>,
@@ -544,26 +614,18 @@ pub mod code_generator_response {
} }
impl<'a> File<'a> { impl<'a> File<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> { pub fn new(data: &'a [u8]) -> crate::runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?; let accessor = crate::runtime::ProtoAccessor::new(data)?;
let mut name_offset = None; let mut name_offset = None;
let mut insertion_point_offset = None; let mut insertion_point_offset = None;
let mut content_offset = None; let mut content_offset = None;
let mut generated_code_info_offset = None; let mut generated_code_info_offset = None;
for item in accessor.fields() { for item in accessor.fields() {
let (offset, tag, _) = item?; let (offset, tag, _) = item?;
if tag.field_number == 1 { if tag.field_number == 1 { name_offset = Some(offset); }
name_offset = Some(offset); if tag.field_number == 2 { insertion_point_offset = Some(offset); }
} if tag.field_number == 15 { content_offset = Some(offset); }
if tag.field_number == 2 { if tag.field_number == 16 { generated_code_info_offset = Some(offset); }
insertion_point_offset = Some(offset);
}
if tag.field_number == 15 {
content_offset = Some(offset);
}
if tag.field_number == 16 {
generated_code_info_offset = Some(offset);
}
} }
Ok(Self { Ok(Self {
@@ -575,45 +637,62 @@ pub mod code_generator_response {
}) })
} }
pub fn name(&self) -> roto_runtime::Result<&'a str> { pub fn name(&self) -> crate::runtime::Result<&'a str> {
let offset = self let offset = self.name_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
.name_offset
.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation) str::from_utf8(bytes).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
} }
pub fn insertion_point(&self) -> roto_runtime::Result<&'a str> { pub fn name_or_default(&self) -> crate::runtime::Result<&'a str> {
let offset = self self.name().or(Ok(""))
.insertion_point_offset
.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
} }
pub fn content(&self) -> roto_runtime::Result<&'a str> { pub fn has_name(&self) -> bool { self.name_offset.is_some() }
let offset = self
.content_offset pub fn insertion_point(&self) -> crate::runtime::Result<&'a str> {
.ok_or(roto_runtime::RotoError::FieldNotFound)?; let offset = self.insertion_point_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation) str::from_utf8(bytes).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
} }
pub fn generated_code_info(&self) -> roto_runtime::Result<&'a [u8]> { pub fn insertion_point_or_default(&self) -> crate::runtime::Result<&'a str> {
let offset = self self.insertion_point().or(Ok(""))
.generated_code_info_offset }
.ok_or(roto_runtime::RotoError::FieldNotFound)?;
pub fn has_insertion_point(&self) -> bool { self.insertion_point_offset.is_some() }
pub fn content(&self) -> crate::runtime::Result<&'a str> {
let offset = self.content_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::runtime::RotoError::WireFormatViolation)
}
pub fn content_or_default(&self) -> crate::runtime::Result<&'a str> {
self.content().or(Ok(""))
}
pub fn has_content(&self) -> bool { self.content_offset.is_some() }
pub fn generated_code_info(&self) -> crate::runtime::Result<&'a [u8]> {
let offset = self.generated_code_info_offset.ok_or(crate::runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?; let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes) Ok(bytes)
} }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> { pub fn generated_code_info_or_default(&self) -> crate::runtime::Result<&'a [u8]> {
self.generated_code_info().or(Ok(&[]))
}
pub fn has_generated_code_info(&self) -> bool { self.generated_code_info_offset.is_some() }
pub fn raw_fields(&self) -> crate::runtime::RawFieldIterator<'a> {
self.accessor.raw_fields() self.accessor.raw_fields()
} }
} }
pub struct FileBuilder<'b> { pub struct FileBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>, builder: crate::runtime::ProtoBuilder<'b>,
name_written: bool, name_written: bool,
insertion_point_written: bool, insertion_point_written: bool,
content_written: bool, content_written: bool,
@@ -623,7 +702,7 @@ pub mod code_generator_response {
impl<'b> FileBuilder<'b> { impl<'b> FileBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> FileBuilder<'_> { pub fn builder(buf: &mut [u8]) -> FileBuilder<'_> {
FileBuilder { FileBuilder {
builder: roto_runtime::ProtoBuilder::new(buf), builder: crate::runtime::ProtoBuilder::new(buf),
name_written: false, name_written: false,
insertion_point_written: false, insertion_point_written: false,
content_written: false, content_written: false,
@@ -631,31 +710,31 @@ pub mod code_generator_response {
} }
} }
pub fn name(mut self, value: &str) -> roto_runtime::Result<Self> { pub fn name(mut self, value: &str) -> crate::runtime::Result<Self> {
self.builder.write_string(1, value)?; self.builder.write_string(1, value)?;
self.name_written = true; self.name_written = true;
Ok(self) Ok(self)
} }
pub fn insertion_point(mut self, value: &str) -> roto_runtime::Result<Self> { pub fn insertion_point(mut self, value: &str) -> crate::runtime::Result<Self> {
self.builder.write_string(2, value)?; self.builder.write_string(2, value)?;
self.insertion_point_written = true; self.insertion_point_written = true;
Ok(self) Ok(self)
} }
pub fn content(mut self, value: &str) -> roto_runtime::Result<Self> { pub fn content(mut self, value: &str) -> crate::runtime::Result<Self> {
self.builder.write_string(15, value)?; self.builder.write_string(15, value)?;
self.content_written = true; self.content_written = true;
Ok(self) Ok(self)
} }
pub fn generated_code_info(mut self, value: &[u8]) -> roto_runtime::Result<Self> { pub fn generated_code_info(mut self, value: &[u8]) -> crate::runtime::Result<Self> {
self.builder.write_bytes(16, value)?; self.builder.write_bytes(16, value)?;
self.generated_code_info_written = true; self.generated_code_info_written = true;
Ok(self) Ok(self)
} }
pub fn with(mut self, msg: &File<'_>) -> roto_runtime::Result<Self> { pub fn with(mut self, msg: &File<'_>) -> crate::runtime::Result<Self> {
for item in msg.raw_fields() { for item in msg.raw_fields() {
let (field_number, raw_bytes) = item?; let (field_number, raw_bytes) = item?;
let is_written = match field_number { let is_written = match field_number {
@@ -672,8 +751,31 @@ pub mod code_generator_response {
Ok(self) Ok(self)
} }
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> { pub fn finish(self) -> crate::runtime::Result<&'b mut [u8]> {
self.builder.finish() self.builder.finish()
} }
} }
pub struct OwnedFile {
pub data: bytes::Bytes,
} }
impl crate::runtime::RotoOwned for OwnedFile {
type Reader<'a> = File<'a>;
fn reader(&self) -> File<'_> {
File::new(&self.data).expect("failed to create reader")
}
}
impl crate::runtime::RotoMessage for OwnedFile {
fn decode(buf: bytes::Bytes) -> crate::runtime::Result<Self> {
Ok(OwnedFile { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
}
File diff suppressed because it is too large Load Diff
+2
View File
@@ -1,2 +1,4 @@
pub mod generator; pub mod generator;
pub mod google; pub mod google;
pub mod runtime;
+921
View File
@@ -0,0 +1,921 @@
use core::fmt;
use bytes::BufMut;
pub struct MapFieldIterator<'a> {
inner: RepeatedFieldIterator<'a>,
}
impl<'a> MapFieldIterator<'a> {
pub fn new(inner: RepeatedFieldIterator<'a>) -> Self {
Self { inner }
}
}
impl<'a> Iterator for MapFieldIterator<'a> {
type Item = Result<(&'a [u8], &'a [u8])>;
fn next(&mut self) -> Option<Self::Item> {
match self.inner.next() {
Some(Ok((value, _wire_type))) => {
let accessor = ProtoAccessor::new(value).ok()?;
let (key_bytes, _) = accessor.get_value(1).ok()?;
let (val_bytes, _) = accessor.get_value(2).ok()?;
Some(Ok((key_bytes, val_bytes)))
}
Some(Err(e)) => Some(Err(e)),
None => None,
}
}
}
#[derive(Debug, PartialEq)]
pub enum RotoError {
UnexpectedEndOfBuffer,
InvalidVarint,
InvalidWireType(u8),
BufferOverflow,
FieldNotFound,
WireFormatViolation,
}
impl fmt::Display for RotoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RotoError::UnexpectedEndOfBuffer => write!(f, "Unexpected end of buffer"),
RotoError::InvalidVarint => write!(f, "Invalid varint encoding"),
RotoError::InvalidWireType(t) => write!(f, "Invalid wire type: {t}"),
RotoError::BufferOverflow => write!(f, "Buffer overflow during write"),
RotoError::FieldNotFound => write!(f, "Requested field not found in message"),
RotoError::WireFormatViolation => write!(f, "Wire format violation"),
}
}
}
impl std::error::Error for RotoError {}
pub type Result<T> = core::result::Result<T, RotoError>;
pub trait RotoOwned {
type Reader<'a> where Self: 'a;
fn reader(&self) -> Self::Reader<'_>;
}
pub trait RotoMessage: Sized {
fn decode(buf: bytes::Bytes) -> Result<Self>;
fn bytes(&self) -> bytes::Bytes;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WireType {
Varint = 0,
Fixed64 = 1,
LengthDelimited = 2,
StartGroup = 3, // Deprecated
EndGroup = 4, // Deprecated
Fixed32 = 5,
}
impl WireType {
pub fn from_u8(value: u8) -> Result<Self> {
match value {
0 => Ok(WireType::Varint),
1 => Ok(WireType::Fixed64),
2 => Ok(WireType::LengthDelimited),
3 => Ok(WireType::StartGroup),
4 => Ok(WireType::EndGroup),
5 => Ok(WireType::Fixed32),
_ => Err(RotoError::InvalidWireType(value)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Tag {
pub field_number: u32,
pub wire_type: WireType,
}
impl Tag {
/// Decodes a tag from the buffer, returning the tag and the number of bytes read.
pub fn decode(data: &[u8]) -> Result<(Self, usize)> {
let (val, len) = read_varint(data)?;
let wire_type_raw = (val & 0x7) as u8;
let field_number = (val >> 3) as u32;
Ok((
Tag {
field_number,
wire_type: WireType::from_u8(wire_type_raw)?,
},
len,
))
}
/// Encodes a tag into the provided buffer.
pub fn encode(field_number: u32, wire_type: WireType, buf: &mut [u8]) -> Result<usize> {
let val = ((field_number as u64) << 3) | (wire_type as u64);
write_varint(val, buf)
}
}
/// Reads a varint from the start of the buffer.
pub fn read_varint(data: &[u8]) -> Result<(u64, usize)> {
let mut result = 0u64;
let mut shift = 0;
let mut bytes_read = 0;
for &byte in data {
bytes_read += 1;
if bytes_read > 10 {
return Err(RotoError::InvalidVarint);
}
let value = (byte & 0x7F) as u64;
if shift >= 64 {
return Err(RotoError::InvalidVarint);
}
result |= value << shift;
shift += 7;
if (byte & 0x80) == 0 {
return Ok((result, bytes_read));
}
}
Err(RotoError::UnexpectedEndOfBuffer)
}
/// Writes a varint into the buffer.
pub fn write_varint(mut value: u64, buf: &mut [u8]) -> Result<usize> {
let mut bytes_written = 0;
while value >= 0x80 {
if bytes_written >= buf.len() {
return Err(RotoError::BufferOverflow);
}
buf[bytes_written] = (value as u8 & 0x7F) | 0x80;
value >>= 7;
bytes_written += 1;
}
if bytes_written >= buf.len() {
return Err(RotoError::BufferOverflow);
}
buf[bytes_written] = value as u8;
bytes_written += 1;
Ok(bytes_written)
}
/// Returns the number of bytes that should be skipped for a given wire type and the current data slice.
pub fn skip_value(wire_type: WireType, data: &[u8]) -> Result<usize> {
match wire_type {
WireType::Varint => {
let (_, len) = read_varint(data)?;
Ok(len)
}
WireType::Fixed64 => {
if data.len() < 8 {
return Err(RotoError::UnexpectedEndOfBuffer);
}
Ok(8)
}
WireType::LengthDelimited => {
let (len, varint_len) = read_varint(data)?;
let total_len = varint_len + len as usize;
if data.len() < total_len {
return Err(RotoError::UnexpectedEndOfBuffer);
}
Ok(total_len)
}
WireType::Fixed32 => {
if data.len() < 4 {
return Err(RotoError::UnexpectedEndOfBuffer);
}
Ok(4)
}
WireType::StartGroup | WireType::EndGroup => {
// These are deprecated and not fully supported in this runtime.
Err(RotoError::WireFormatViolation)
}
}
}
pub struct ProtoAccessor<'a> {
data: &'a [u8],
}
impl<'a> ProtoAccessor<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self { data })
}
/// Returns an iterator over all fields in the message.
pub fn fields(&self) -> FieldIterator<'a> {
FieldIterator {
data: self.data,
cursor: 0,
}
}
/// Returns the value and wire type of the last occurrence of the specified field.
pub fn get_value(&self, field_number: u32) -> Result<(&'a [u8], WireType)> {
let mut last_value = None;
for item in self.fields() {
let (_offset, tag, value) = item?;
if tag.field_number == field_number {
last_value = Some((value, tag.wire_type));
}
}
last_value.ok_or(RotoError::FieldNotFound)
}
/// Returns an iterator that scans the entire buffer for all occurrences of the specified field.
pub fn iter_repeated(&self, field_number: u32) -> RepeatedFieldIterator<'a> {
RepeatedFieldIterator::new(self.data, field_number)
}
/// Returns the value and wire type of a field at a specific offset.
pub fn get_value_at(&self, offset: usize) -> Result<(&'a [u8], WireType)> {
if offset >= self.data.len() {
return Err(RotoError::UnexpectedEndOfBuffer);
}
let (tag, tag_len) = Tag::decode(&self.data[offset..])?;
let cursor_after_tag = offset + tag_len;
if cursor_after_tag > self.data.len() {
return Err(RotoError::UnexpectedEndOfBuffer);
}
let value_len = skip_value(tag.wire_type, &self.data[cursor_after_tag..])?;
let (value_offset, actual_value_len) = match tag.wire_type {
WireType::LengthDelimited => {
let (_, varint_len) = read_varint(&self.data[cursor_after_tag..])?;
(cursor_after_tag + varint_len, value_len - varint_len)
}
_ => (cursor_after_tag, value_len),
};
Ok((
&self.data[value_offset..value_offset + actual_value_len],
tag.wire_type,
))
}
/// Returns an iterator that scans a specific range of the buffer for all occurrences of the specified field.
pub fn iter_repeated_range(
&self,
field_number: u32,
start: usize,
end: usize,
) -> RepeatedFieldIterator<'a> {
RepeatedFieldIterator::new_range(self.data, field_number, start, end)
}
/// Returns an iterator that yields `(field_number, raw_bytes)` for every
/// field in the message. `raw_bytes` is the complete on-wire encoding
/// (tag + value, including any length prefix), suitable for passing
/// directly to `ProtoBuilder::write_raw`.
pub fn raw_fields(&self) -> RawFieldIterator<'a> {
RawFieldIterator {
data: self.data,
cursor: 0,
}
}
}
pub struct FieldIterator<'a> {
data: &'a [u8],
cursor: usize,
}
impl<'a> Iterator for FieldIterator<'a> {
type Item = Result<(usize, Tag, &'a [u8])>;
fn next(&mut self) -> Option<Self::Item> {
if self.cursor >= self.data.len() {
return None;
}
let (tag, tag_len) = match Tag::decode(&self.data[self.cursor..]) {
Ok(t) => t,
Err(e) => {
self.cursor = self.data.len();
return Some(Err(e));
}
};
let cursor_after_tag = self.cursor + tag_len;
if cursor_after_tag > self.data.len() {
self.cursor = self.data.len();
return Some(Err(RotoError::UnexpectedEndOfBuffer));
}
let value_len = match skip_value(tag.wire_type, &self.data[cursor_after_tag..]) {
Ok(l) => l,
Err(e) => {
self.cursor = self.data.len();
return Some(Err(e));
}
};
let (value_offset, actual_value_len) = match tag.wire_type {
WireType::LengthDelimited => {
let (_, varint_len) = match read_varint(&self.data[cursor_after_tag..]) {
Ok(v) => v,
Err(e) => {
self.cursor = self.data.len();
return Some(Err(e));
}
};
(cursor_after_tag + varint_len, value_len - varint_len)
}
_ => (cursor_after_tag, value_len),
};
self.cursor = cursor_after_tag + value_len;
Some(Ok((
self.cursor - tag_len - value_len,
tag,
&self.data[value_offset..value_offset + actual_value_len],
)))
}
}
pub struct RepeatedFieldIterator<'a> {
iterator: FieldIterator<'a>,
field_number: u32,
end_offset: Option<usize>,
}
impl<'a> RepeatedFieldIterator<'a> {
pub fn new(data: &'a [u8], field_number: u32) -> Self {
Self {
iterator: FieldIterator { data, cursor: 0 },
field_number,
end_offset: None,
}
}
pub fn new_range(data: &'a [u8], field_number: u32, start: usize, end: usize) -> Self {
Self {
iterator: FieldIterator {
data,
cursor: start,
},
field_number,
end_offset: Some(end),
}
}
}
impl<'a> Iterator for RepeatedFieldIterator<'a> {
type Item = Result<(&'a [u8], WireType)>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(item) = self.iterator.next() {
match item {
Ok((offset, tag, value)) if tag.field_number == self.field_number => {
if let Some(end) = self.end_offset {
if offset > end {
return None;
}
}
return Some(Ok((value, tag.wire_type)));
}
Ok(_) => continue,
Err(e) => return Some(Err(e)),
}
}
None
}
}
/// An iterator that yields `(field_number, raw_bytes)` for every field in a
/// protobuf message, where `raw_bytes` is the complete on-wire encoding of the
/// field: tag varint + value bytes (including the length prefix for
/// length-delimited fields). This is the slice needed by
/// `ProtoBuilder::write_raw` to copy a field verbatim.
pub struct RawFieldIterator<'a> {
data: &'a [u8],
cursor: usize,
}
impl<'a> Iterator for RawFieldIterator<'a> {
type Item = Result<(u32, &'a [u8])>;
fn next(&mut self) -> Option<Self::Item> {
if self.cursor >= self.data.len() {
return None;
}
let field_start = self.cursor;
let (tag, tag_len) = match Tag::decode(&self.data[self.cursor..]) {
Ok(t) => t,
Err(e) => {
self.cursor = self.data.len();
return Some(Err(e));
}
};
let cursor_after_tag = self.cursor + tag_len;
if cursor_after_tag > self.data.len() {
self.cursor = self.data.len();
return Some(Err(RotoError::UnexpectedEndOfBuffer));
}
let value_len = match skip_value(tag.wire_type, &self.data[cursor_after_tag..]) {
Ok(l) => l,
Err(e) => {
self.cursor = self.data.len();
return Some(Err(e));
}
};
self.cursor = cursor_after_tag + value_len;
Some(Ok((tag.field_number, &self.data[field_start..self.cursor])))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "alloc")]
use alloc::{vec, vec::{Vec}};
#[test]
fn test_varint_read_write() {
let mut buf = [0u8; 10];
let val = 300u64;
let len = write_varint(val, &mut buf).unwrap();
assert_eq!(len, 2);
assert_eq!(&buf[..2], &[0xAC, 0x02]);
let (read_val, read_len) = read_varint(&buf[..2]).unwrap();
assert_eq!(read_val, val);
assert_eq!(read_len, 2);
}
#[test]
fn test_tag_decode() {
// Field 1, WireType Varint: (1 << 3) | 0 = 8
let data = [8u8];
let (tag, len) = Tag::decode(&data).unwrap();
assert_eq!(tag.field_number, 1);
assert_eq!(tag.wire_type, WireType::Varint);
assert_eq!(len, 1);
// Field 15, WireType LengthDelimited: (15 << 3) | 2 = 120 | 2 = 122
let data2 = [122u8];
let (tag2, len2) = Tag::decode(&data2).unwrap();
assert_eq!(tag2.field_number, 15);
assert_eq!(tag2.wire_type, WireType::LengthDelimited);
assert_eq!(len2, 1);
}
#[test]
fn test_skip_value() {
// Varint: 300 (2 bytes)
let data_varint = [0xAC, 0x02];
assert_eq!(skip_value(WireType::Varint, &data_varint).unwrap(), 2);
// Fixed32: 4 bytes
let data_fixed32 = [0u8; 4];
assert_eq!(skip_value(WireType::Fixed32, &data_fixed32).unwrap(), 4);
// Length delimited: len=3, data=[1,2,3] (1 byte varint for length + 3 bytes)
let data_len = [3, 1, 2, 3];
assert_eq!(skip_value(WireType::LengthDelimited, &data_len).unwrap(), 4);
}
#[test]
fn test_accessor_basic() {
// Field 1 (Varint): 150
// Tag: (1 << 3) | 0 = 8. Value: 150 = [150, 1]
// Field 2 (LengthDelimited): "hi"
// Tag: (2 << 3) | 2 = 18. Length: 2. Value: [104, 105]
let data = [8, 150, 1, 18, 2, 104, 105];
let acc = ProtoAccessor::new(&data).unwrap();
let (val1, type1) = acc.get_value(1).unwrap();
assert_eq!(type1, WireType::Varint);
assert_eq!(val1, &[150, 1]);
let (val2, type2) = acc.get_value(2).unwrap();
assert_eq!(type2, WireType::LengthDelimited);
assert_eq!(val2, &[104, 105]);
}
#[test]
fn test_accessor_repeated() {
// Field 1: 10, Field 1: 20, Field 1: 30
// Tags: 8, 8, 8. Values: 10, 20, 30
let data = [8, 10, 8, 20, 8, 30];
let acc = ProtoAccessor::new(&data).unwrap();
// Last value should be 30
let (val, _) = acc.get_value(1).unwrap();
assert_eq!(val, &[30]);
// Iteration should find all three
let results: Vec<_> = acc.iter_repeated(1).collect();
assert_eq!(results.len(), 3);
assert_eq!(results[0].as_ref().unwrap().0, &[10]);
assert_eq!(results[1].as_ref().unwrap().0, &[20]);
assert_eq!(results[2].as_ref().unwrap().0, &[30]);
}
#[test]
fn test_builder_basic() {
let mut buf = [0u8; 1024];
let mut builder = ProtoBuilder::new(&mut buf);
builder.write_string(1, "hello").unwrap();
builder.write_int32(2, 42).unwrap();
let data = builder.finish().unwrap();
let acc = ProtoAccessor::new(data).unwrap();
let (val1, _) = acc.get_value(1).unwrap();
assert_eq!(val1, "hello".as_bytes());
let (val2, _) = acc.get_value(2).unwrap();
assert_eq!(val2, &[42]);
}
#[test]
fn test_builder_overflow() {
let mut buf = [0u8; 2];
let mut builder = ProtoBuilder::new(&mut buf);
let result = builder.write_string(1, "too long");
assert_eq!(result, Err(RotoError::BufferOverflow));
}
#[test]
fn test_raw_field_iterator_yields_correct_bytes() {
// Build: field 1 = string "hi", field 2 = int32 42
let mut buf = [0u8; 64];
let mut builder = ProtoBuilder::new(&mut buf);
builder.write_string(1, "hi").unwrap();
builder.write_int32(2, 42).unwrap();
let data = builder.finish().unwrap().to_vec();
let acc = ProtoAccessor::new(&data).unwrap();
let raw: Vec<_> = acc.raw_fields().collect();
assert_eq!(raw.len(), 2);
// Field 1: tag = (1 << 3) | 2 = 0x0A, len varint = 0x02, "hi" = [0x68, 0x69]
let (fn1, bytes1) = raw[0].as_ref().unwrap();
assert_eq!(*fn1, 1);
assert_eq!(*bytes1, [0x0A, 0x02, b'h', b'i']);
// Field 2: tag = (2 << 3) | 0 = 0x10, varint 42 = 0x2A
let (fn2, bytes2) = raw[1].as_ref().unwrap();
assert_eq!(*fn2, 2);
assert_eq!(*bytes2, [0x10, 0x2A]);
}
#[test]
fn test_write_raw_copies_field_verbatim() {
// Build source: field 1 = string "hello", field 2 = int32 99
let mut src_buf = [0u8; 64];
let mut src_builder = ProtoBuilder::new(&mut src_buf);
src_builder.write_string(1, "hello").unwrap();
src_builder.write_int32(2, 99).unwrap();
let src_data = src_builder.finish().unwrap().to_vec();
// Copy every raw field verbatim into a new buffer
let src_acc = ProtoAccessor::new(&src_data).unwrap();
let mut dst_buf = [0u8; 64];
let mut dst_builder = ProtoBuilder::new(&mut dst_buf);
for item in src_acc.raw_fields() {
let (_, raw_bytes) = item.unwrap();
dst_builder.write_raw(raw_bytes).unwrap();
}
let dst_data = dst_builder.finish().unwrap();
// The copy must be byte-identical to the source
assert_eq!(dst_data, src_data.as_slice());
}
#[test]
fn test_with_pattern_copies_unseen_fields() {
// Build an existing source message with 3 fields
let mut src_buf = [0u8; 128];
let mut src_builder = ProtoBuilder::new(&mut src_buf);
src_builder.write_string(1, "original").unwrap();
src_builder.write_int32(2, 99).unwrap();
src_builder.write_varint(3, 1u64).unwrap(); // bool
let src_data = src_builder.finish().unwrap().to_vec();
let src_acc = ProtoAccessor::new(&src_data).unwrap();
// Simulate what a generated `with` method does:
// field 1 was explicitly written; fields 2 and 3 come from source.
let field1_written = true;
let field2_written = false;
let field3_written = false;
let mut dst_buf = [0u8; 128];
let mut dst_builder = ProtoBuilder::new(&mut dst_buf);
dst_builder.write_string(1, "updated").unwrap();
for item in src_acc.raw_fields() {
let (field_number, raw_bytes) = item.unwrap();
let is_written = match field_number {
1 => field1_written,
2 => field2_written,
3 => field3_written,
_ => false,
};
if !is_written {
dst_builder.write_raw(raw_bytes).unwrap();
}
}
let dst_data = dst_builder.finish().unwrap();
let dst_acc = ProtoAccessor::new(dst_data).unwrap();
// Field 1: overridden value
let (val1, _) = dst_acc.get_value(1).unwrap();
assert_eq!(val1, b"updated");
// Field 2: copied from source
let (val2, _) = dst_acc.get_value(2).unwrap();
let (v2, _) = read_varint(val2).unwrap();
assert_eq!(v2 as i32, 99);
// Field 3: copied from source
let (val3, _) = dst_acc.get_value(3).unwrap();
let (v3, _) = read_varint(val3).unwrap();
assert_eq!(v3, 1u64);
}
#[test]
fn test_protoc_binary_compatibility() {
let data = include_bytes!("../data/test_data.pb");
let acc = ProtoAccessor::new(data).unwrap();
// 1. Varints (Integers, Booleans, Enums)
let (val_i32, type_i32) = acc.get_value(3).expect("i32_val not found");
assert_eq!(type_i32, WireType::Varint);
let (v, _) = read_varint(val_i32).unwrap();
assert_eq!(v, 42);
let (val_b, type_b) = acc.get_value(13).expect("b_val not found");
assert_eq!(type_b, WireType::Varint);
let (v_b, _) = read_varint(val_b).unwrap();
assert_eq!(v_b, 1); // true
let (val_status, type_status) = acc.get_value(16).expect("status not found");
assert_eq!(type_status, WireType::Varint);
let (v_s, _) = read_varint(val_status).unwrap();
assert_eq!(v_s, 1); // ACTIVE
// 2. Length Delimited (Strings, Bytes)
let (val_s, type_s) = acc.get_value(14).expect("s_val not found");
assert_eq!(type_s, WireType::LengthDelimited);
assert_eq!(val_s, "Hello Roto!".as_bytes());
// 3. Fixed Width (Floats)
let (val_f, type_f) = acc.get_value(2).expect("f_val not found");
assert_eq!(type_f, WireType::Fixed32);
let f_val = f32::from_le_bytes(val_f.try_into().expect("Expected 4 bytes for f32"));
assert!((f_val - 2.71828).abs() < 1e-5);
// 4. Repeated Fields
// Note: primitive repeated fields are packed in proto3, so we iterate over the blob
let mut i32_vals = Vec::new();
for item in acc.iter_repeated(17) {
let (blob, _) = item.expect("Failed to decode repeated i32");
let mut cursor = 0;
while cursor < blob.len() {
let (v, len) = read_varint(&blob[cursor..]).unwrap();
i32_vals.push(v);
cursor += len;
}
}
assert_eq!(i32_vals, vec![1, 2, 3, 4, 5]);
let repeated_strings: Vec<_> = acc
.iter_repeated(18)
.map(|r| {
let (val, _) = r.expect("Failed to decode repeated string");
core::str::from_utf8(val).expect("Invalid utf8")
})
.collect();
assert_eq!(repeated_strings, vec!["one", "two", "three"]);
let repeated_nested: Vec<_> = acc
.iter_repeated(19)
.map(|r| {
let (val, _) = r.expect("Failed to decode repeated nested");
let nested_acc = ProtoAccessor::new(val).unwrap();
let (id_val, _) = nested_acc.get_value(1).expect("Nested id not found");
let (id, _) = read_varint(id_val).unwrap();
id
})
.collect();
assert_eq!(repeated_nested, vec![101, 102]);
// 5. Single Nested Message
let (val_nested, type_nested) = acc.get_value(20).expect("single_nested not found");
assert_eq!(type_nested, WireType::LengthDelimited);
let nested_acc = ProtoAccessor::new(val_nested).unwrap();
let (val_id, _) = nested_acc.get_value(1).expect("Nested id not found");
let (id, _) = read_varint(val_id).unwrap();
assert_eq!(id, 200);
// Validate that fields appear in the expected relative order
let field_numbers: Vec<u32> = acc
.fields()
.map(|r| r.expect("Failed to decode field").1.field_number)
.collect();
let essential_fields = [1, 2, 3, 14, 16, 20];
let mut last_field = 0;
let mut found_count = 0;
for &f in &field_numbers {
if essential_fields.contains(&f) {
assert!(
f >= last_field,
"Fields appeared out of order: {} came after {}",
f,
last_field
);
last_field = f;
found_count += 1;
}
}
assert_eq!(found_count, essential_fields.len());
}
}
pub struct ProtoBuilder<'a> {
buf: &'a mut [u8],
pos: usize,
}
impl<'a> ProtoBuilder<'a> {
pub fn new(buf: &'a mut [u8]) -> Self {
Self { buf, pos: 0 }
}
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.append_bytes(&temp[..len])
}
fn append_bytes(&mut self, bytes: &[u8]) -> Result<()> {
if self.pos + bytes.len() > self.buf.len() {
return Err(RotoError::BufferOverflow);
}
self.buf[self.pos..self.pos + bytes.len()].copy_from_slice(bytes);
self.pos += bytes.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.append_bytes(&temp[..len])
}
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.append_bytes(&len_buf[..len_len])?;
self.append_bytes(bytes)
}
pub fn write_fixed32(&mut self, field_number: u32, value: u32) -> Result<()> {
self.write_tag(field_number, WireType::Fixed32)?;
self.append_bytes(&value.to_le_bytes())
}
pub fn write_fixed64(&mut self, field_number: u32, value: u64) -> Result<()> {
self.write_tag(field_number, WireType::Fixed64)?;
self.append_bytes(&value.to_le_bytes())
}
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.append_bytes(&len_buf[..len_len])?;
self.append_bytes(value)
}
/// Appends a pre-encoded field (tag + value bytes) verbatim into the
/// buffer. Use this together with `ProtoAccessor::raw_fields` to copy
/// fields from an existing message into a builder without re-encoding them.
pub fn write_raw(&mut self, raw_bytes: &[u8]) -> Result<()> {
self.append_bytes(raw_bytes)
}
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.append_bytes(&len_buf[..len_len])?;
self.append_bytes(key_encoded)?;
self.append_bytes(value_encoded)?;
Ok(())
}
pub fn finish(self) -> Result<&'a mut [u8]> {
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(())
}
}
+20 -12
View File
@@ -37,8 +37,9 @@ fn test_generated_code_builds() {
); );
// 2. Setup a temporary Cargo project to verify the code builds // 2. Setup a temporary Cargo project to verify the code builds
let root = std::env::current_dir().expect("Failed to get current directory"); let codegen_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let temp_project_dir = root.join("test_gen_project"); let project_root = codegen_root.parent().expect("Failed to get project root");
let temp_project_dir = std::path::PathBuf::from("/tmp/roto_test_gen_project");
// Clean up previous runs // Clean up previous runs
if temp_project_dir.exists() { if temp_project_dir.exists() {
@@ -47,8 +48,7 @@ fn test_generated_code_builds() {
// Create new library project // Create new library project
let status = Command::new("cargo") let status = Command::new("cargo")
.args(["new", "--lib", "test_gen_project"]) .args(["new", "--lib", temp_project_dir.to_str().expect("Invalid path")])
.current_dir(&root)
.status() .status()
.expect("Failed to run cargo new"); .expect("Failed to run cargo new");
assert!(status.success(), "cargo new failed"); assert!(status.success(), "cargo new failed");
@@ -58,31 +58,39 @@ fn test_generated_code_builds() {
let cargo_toml_content = let cargo_toml_content =
fs::read_to_string(&cargo_toml_path).expect("Failed to read Cargo.toml"); fs::read_to_string(&cargo_toml_path).expect("Failed to read Cargo.toml");
let updated_cargo_toml = format!( let updated_cargo_toml = format!(
"{}\n\nroto-codegen = {{ path = \"..\" }}\nroto-runtime = {{ path = \"../../runtime\" }}\n\n[workspace]\n", "{}\n\nroto-codegen = {{ path = \"{}\" }}\nroto-runtime = {{ path = \"{}\" }}\nroto-tonic = {{ path = \"{}\" }}\nbytes = \"1.7\"\ntonic = \"0.12\"\ntokio-stream = \"0.1\"\ntower = \"0.4\"\nfutures-util = \"0.3\"\nhttp-body-util = \"0.1\"\nhttp-body = \"1.0\"\n\n[workspace]\n",
cargo_toml_content cargo_toml_content,
codegen_root.to_string_lossy(),
project_root.join("runtime").to_string_lossy(),
project_root.join("roto-tonic").to_string_lossy()
); );
fs::write(cargo_toml_path, updated_cargo_toml).expect("Failed to write Cargo.toml"); fs::write(cargo_toml_path, updated_cargo_toml).expect("Failed to write Cargo.toml");
// 4. Write the generated code to src/lib.rs // 4. Write the generated code to src/lib.rs
// The generated code uses `use crate::{...}`, but it's now in a separate crate. // The generated code uses `use crate::{...}`, but it's now in a separate crate.
// Replace `crate` with `roto` to reference the types in the dependency. // Replace `crate` with `roto_tonic` to reference the types in the dependency.
let mut all_code = String::new(); let mut all_code = String::new();
for (_, content) in generated_files { for (_, content) in generated_files {
all_code.push_str(&content); let replaced = content.replace("use crate::{BufferPool, StatusBody};", "use roto_tonic::{BufferPool, StatusBody};");
all_code.push_str(&replaced);
all_code.push_str("\n"); all_code.push_str("\n");
} }
let lib_path = temp_project_dir.join("src/lib.rs"); let lib_path = temp_project_dir.join("src/lib.rs");
fs::write(lib_path, all_code).expect("Failed to write generated code to src/lib.rs"); fs::write(lib_path, all_code).expect("Failed to write generated code to src/lib.rs");
// 5. Attempt to build the project // 5. Attempt to build the project
let build_status = Command::new("cargo") let build_output = Command::new("cargo")
.args(["--offline", "build"]) .args(["build"])
.current_dir(&temp_project_dir) .current_dir(&temp_project_dir)
.status() .output()
.expect("Failed to run cargo build"); .expect("Failed to run cargo build");
if !build_output.status.success() {
eprintln!("Cargo build failed output:\n{}", String::from_utf8_lossy(&build_output.stderr));
}
assert!( assert!(
build_status.success(), build_output.status.success(),
"The generated Rust code failed to build in a standalone project!" "The generated Rust code failed to build in a standalone project!"
); );
} }
Binary file not shown.
+76
View File
@@ -0,0 +1,76 @@
use roto_codegen::google::protobuf::descriptor::FileDescriptorSet;
use std::fs;
use std::process::Command;
#[test]
fn test_helloworld_generated_code_builds() {
// 1. Load FileDescriptorSet from helloworld.desc
let desc_path = "helloworld.desc";
// Note: This assumes helloworld.desc is in the working directory of the test.
// We might need to provide the full path or copy it to the test data directory.
let data = fs::read(desc_path).expect("Failed to read helloworld.desc");
let set = FileDescriptorSet::new(&data)
.expect("Failed to create FileDescriptorSet from helloworld.desc");
let generated_files = roto_codegen::generator::generate_rust_code(&set, None, false);
assert!(
!generated_files.is_empty(),
"Generated code should not be empty"
);
for (path, content) in &generated_files {
println!("--- File: {} ---\n{}", path, content);
}
// 2. Setup a temporary Cargo project to verify the code builds
let codegen_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let project_root = codegen_root.parent().expect("Failed to get project root");
let temp_project_dir = std::path::PathBuf::from("/tmp/roto_helloworld_gen_project");
// Clean up previous runs
if temp_project_dir.exists() {
fs::remove_dir_all(&temp_project_dir).expect("Failed to clean up temp project directory");
}
// Create new library project
let status = Command::new("cargo")
.args(["new", "--lib", temp_project_dir.to_str().expect("Invalid path")])
.status()
.expect("Failed to run cargo new");
assert!(status.success(), "cargo new failed");
// 3. Configure the project to depend on the current roto crate
let cargo_toml_path = temp_project_dir.join("Cargo.toml");
let cargo_toml_content =
fs::read_to_string(&cargo_toml_path).expect("Failed to read Cargo.toml");
let updated_cargo_toml = format!(
"{}\n\nroto-codegen = {{ path = \"{}\" }}\nroto-runtime = {{ path = \"{}\" }}\nroto-tonic = {{ path = \"{}\" }}\nbytes = \"1.7\"\ntonic = \"0.12\"\ntokio-stream = \"0.1\"\ntower = \"0.4\"\nfutures-util = \"0.3\"\nhttp-body-util = \"0.1\"\nhttp-body = \"1.0\"\n\nhttp = \"1.0\"\n\n[workspace]\n",
cargo_toml_content,
codegen_root.to_string_lossy(),
project_root.join("runtime").to_string_lossy(),
project_root.join("roto-tonic").to_string_lossy()
);
fs::write(cargo_toml_path, updated_cargo_toml).expect("Failed to write Cargo.toml");
// 4. Write the generated code to src/lib.rs
let mut all_code = String::new();
for (_, content) in generated_files {
let replaced = content.replace("use crate::{BufferPool, StatusBody};", "use roto_tonic::{BufferPool, StatusBody};");
all_code.push_str(&replaced);
all_code.push_str("\n");
}
let lib_path = temp_project_dir.join("src/lib.rs");
fs::write(lib_path, all_code).expect("Failed to write generated code to src/lib.rs");
// 5. Attempt to build the project
let build_status = Command::new("cargo")
.args(["build"])
.current_dir(&temp_project_dir)
.status()
.expect("Failed to run cargo build");
assert!(
build_status.success(),
"The generated Rust code for helloworld.proto failed to build in a standalone project!"
);
}
+20 -13
View File
@@ -17,8 +17,9 @@ fn test_map_generated_code_builds() {
); );
// 2. Setup a temporary Cargo project to verify the code builds // 2. Setup a temporary Cargo project to verify the code builds
let root = std::env::current_dir().expect("Failed to get current directory"); let codegen_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let temp_project_dir = root.join("test_map_gen_project"); let project_root = codegen_root.parent().expect("Failed to get project root");
let temp_project_dir = std::path::PathBuf::from("/tmp/roto_test_map_gen_project");
// Clean up previous runs // Clean up previous runs
if temp_project_dir.exists() { if temp_project_dir.exists() {
@@ -27,8 +28,7 @@ fn test_map_generated_code_builds() {
// Create new library project // Create new library project
let status = Command::new("cargo") let status = Command::new("cargo")
.args(["new", "--lib", "test_map_gen_project"]) .args(["new", "--lib", temp_project_dir.to_str().expect("Invalid path")])
.current_dir(&root)
.status() .status()
.expect("Failed to run cargo new"); .expect("Failed to run cargo new");
assert!(status.success(), "cargo new failed"); assert!(status.success(), "cargo new failed");
@@ -38,30 +38,37 @@ fn test_map_generated_code_builds() {
let cargo_toml_content = let cargo_toml_content =
fs::read_to_string(&cargo_toml_path).expect("Failed to read Cargo.toml"); fs::read_to_string(&cargo_toml_path).expect("Failed to read Cargo.toml");
let updated_cargo_toml = format!( let updated_cargo_toml = format!(
"{}\n\nroto-codegen = {{ path = \"..\" }}\nroto-runtime = {{ path = \"../../runtime\" }}\n\n[workspace]\n", "{}\n\nroto-codegen = {{ path = \"{}\" }}\nroto-runtime = {{ path = \"{}\" }}\nroto-tonic = {{ path = \"{}\" }}\nbytes = \"1.7\"\ntonic = \"0.12\"\ntokio-stream = \"0.1\"\ntower = \"0.4\"\nfutures-util = \"0.3\"\nhttp-body-util = \"0.1\"\nhttp-body = \"1.0\"\n\n[workspace]\n",
cargo_toml_content cargo_toml_content,
codegen_root.to_string_lossy(),
project_root.join("runtime").to_string_lossy(),
project_root.join("roto-tonic").to_string_lossy()
); );
fs::write(cargo_toml_path, updated_cargo_toml).expect("Failed to write Cargo.toml"); fs::write(cargo_toml_path, updated_cargo_toml).expect("Failed to write Cargo.toml");
// 4. Write the generated code to src/lib.rs // 4. Write the generated code to src/lib.rs
let mut all_code = String::new(); let mut all_code = String::new();
for (_, content) in generated_files { for (_, content) in generated_files {
all_code.push_str(&content); let replaced = content.replace("use crate::{BufferPool, StatusBody};", "use roto_tonic::{BufferPool, StatusBody};");
all_code.push_str(&replaced);
all_code.push_str("\n"); all_code.push_str("\n");
} }
let final_code = all_code.replace("use crate::", "use roto::");
let lib_path = temp_project_dir.join("src/lib.rs"); let lib_path = temp_project_dir.join("src/lib.rs");
fs::write(lib_path, final_code).expect("Failed to write generated code to src/lib.rs"); fs::write(lib_path, all_code).expect("Failed to write generated code to src/lib.rs");
// 5. Attempt to build the project // 5. Attempt to build the project
let build_status = Command::new("cargo") let output = Command::new("cargo")
.args(["--offline", "build"]) .args(["build"])
.current_dir(&temp_project_dir) .current_dir(&temp_project_dir)
.status() .output()
.expect("Failed to run cargo build"); .expect("Failed to run cargo build");
if !output.status.success() {
eprintln!("Cargo build failed:\n{}", String::from_utf8_lossy(&output.stderr));
}
assert!( assert!(
build_status.success(), output.status.success(),
"The generated Rust code for test_map.proto failed to build in a standalone project!" "The generated Rust code for test_map.proto failed to build in a standalone project!"
); );
} }
+13 -15
View File
@@ -17,8 +17,9 @@ fn test_types_generated_code_builds() {
); );
// 2. Setup a temporary Cargo project to verify the code builds // 2. Setup a temporary Cargo project to verify the code builds
let root = std::env::current_dir().expect("Failed to get current directory"); let codegen_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let temp_project_dir = root.join("test_types_gen_project"); let project_root = codegen_root.parent().expect("Failed to get project root");
let temp_project_dir = std::path::PathBuf::from("/tmp/roto_test_types_gen_project");
// Clean up previous runs // Clean up previous runs
if temp_project_dir.exists() { if temp_project_dir.exists() {
@@ -27,8 +28,7 @@ fn test_types_generated_code_builds() {
// Create new library project // Create new library project
let status = Command::new("cargo") let status = Command::new("cargo")
.args(["new", "--lib", "test_types_gen_project"]) .args(["new", "--lib", temp_project_dir.to_str().expect("Invalid path")])
.current_dir(&root)
.status() .status()
.expect("Failed to run cargo new"); .expect("Failed to run cargo new");
assert!(status.success(), "cargo new failed"); assert!(status.success(), "cargo new failed");
@@ -38,29 +38,27 @@ fn test_types_generated_code_builds() {
let cargo_toml_content = let cargo_toml_content =
fs::read_to_string(&cargo_toml_path).expect("Failed to read Cargo.toml"); fs::read_to_string(&cargo_toml_path).expect("Failed to read Cargo.toml");
let updated_cargo_toml = format!( let updated_cargo_toml = format!(
"{}\n\nroto-codegen = {{ path = \"..\" }}\nroto-runtime = {{ path = \"../../runtime\" }}\n\n[workspace]\n", "{}\n\nroto-codegen = {{ path = \"{}\" }}\nroto-runtime = {{ path = \"{}\" }}\nroto-tonic = {{ path = \"{}\" }}\nbytes = \"1.7\"\ntonic = \"0.12\"\ntokio-stream = \"0.1\"\ntower = \"0.4\"\nfutures-util = \"0.3\"\nhttp-body-util = \"0.1\"\nhttp-body = \"1.0\"\n\n[workspace]\n",
cargo_toml_content cargo_toml_content,
codegen_root.to_string_lossy(),
project_root.join("runtime").to_string_lossy(),
project_root.join("roto-tonic").to_string_lossy()
); );
fs::write(cargo_toml_path, updated_cargo_toml).expect("Failed to write Cargo.toml"); fs::write(cargo_toml_path, updated_cargo_toml).expect("Failed to write Cargo.toml");
// 4. Write the generated code to src/lib.rs // 4. Write the generated code to src/lib.rs
let mut all_code = String::new(); let mut all_code = String::new();
for (_, content) in generated_files { for (_, content) in generated_files {
all_code.push_str(&content); let replaced = content.replace("use crate::{BufferPool, StatusBody};", "use roto_tonic::{BufferPool, StatusBody};");
all_code.push_str(&replaced);
all_code.push_str("\n"); all_code.push_str("\n");
} }
// The generated code uses `use crate::{...}`, but it's now in a separate crate.
// Replace `crate` with `roto` to reference the types in the dependency.
// Note: in build_generated_code.rs it does replace("use crate::", "use roto::").
// But here the generated code might not have dependencies since it's a single file.
// However, to be safe and consistent with the template:
let final_code = all_code.replace("use crate::", "use roto::");
let lib_path = temp_project_dir.join("src/lib.rs"); let lib_path = temp_project_dir.join("src/lib.rs");
fs::write(lib_path, final_code).expect("Failed to write generated code to src/lib.rs"); fs::write(lib_path, all_code).expect("Failed to write generated code to src/lib.rs");
// 5. Attempt to build the project // 5. Attempt to build the project
let build_status = Command::new("cargo") let build_status = Command::new("cargo")
.args(["--offline", "build"]) .args(["build"])
.current_dir(&temp_project_dir) .current_dir(&temp_project_dir)
.status() .status()
.expect("Failed to run cargo build"); .expect("Failed to run cargo build");
+35
View File
@@ -0,0 +1,35 @@
[package]
name = "hello-world"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "server"
path = "src/bin/server.rs"
[[bin]]
name = "client"
path = "src/bin/client.rs"
[dependencies]
roto-runtime = { path = "../../runtime" }
roto-tonic = { path = "../../roto-tonic" }
tonic = "0.12"
tokio = { version = "1.38", features = ["full"] }
tokio-stream = "0.1"
bytes = "1.7"
prost = "0.13"
tower = "0.4"
futures-util = "0.3"
http-body-util = "0.1"
http = "1.1"
http-body = "1.0"
async-trait = "0.1"
[build-dependencies]
tonic-build = "0.12"
roto-codegen = { path = "../../codegen" }
[features]
default = ["alloc"]
alloc = []
+20
View File
@@ -0,0 +1,20 @@
# Hello World Example
This example demonstrates a simple gRPC service using `roto`.
## Running the server
```bash
cargo run --bin server
```
## Calling the service
You can use `grpc_cli` to call the `HelloWorld` RPC:
```bash
grpc_cli call [::1]:50051 hello.HelloWorldService.HelloWorld 'name: "World"' \
--protofiles examples/hello_world/proto/hello.proto \
--proto_path examples/hello_world/proto \
--channel_creds_type insecure
```
+27
View File
@@ -0,0 +1,27 @@
fn main() {
let proto_file = "proto/hello.proto";
let out_dir = std::env::var("OUT_DIR").unwrap();
let dest_path = std::path::Path::new(&out_dir).join("hello.rs");
// Find the protoc-gen-roto binary
// Since we added roto-codegen to build-dependencies, it will be built.
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let target_dir = std::path::Path::new(&manifest_dir).join("../../target/debug");
let plugin_path = target_dir.join("protoc-gen-roto");
if !plugin_path.exists() {
panic!("protoc-gen-roto plugin not found at {:?}", plugin_path);
}
let status = std::process::Command::new("protoc")
.arg(format!("--plugin=protoc-gen-roto={}", plugin_path.display()))
.arg(format!("--roto_out={}", out_dir))
.arg(format!("--roto_opt=src=proto")) // Assuming the plugin handles this or we just pass it
.arg(proto_file)
.status()
.expect("Failed to execute protoc");
if !status.success() {
panic!("protoc failed with status {}", status);
}
}
+15
View File
@@ -0,0 +1,15 @@
syntax = "proto3";
package hello;
service HelloWorldService {
rpc HelloWorld (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
+334
View File
@@ -0,0 +1,334 @@
// @generated by protoc-gen-roto — do not edit
#[allow(unused_imports)]
use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};
use core::str;
#[cfg(feature = "alloc")]
use bytes::{Bytes, BytesMut, Buf, BufMut};
pub struct HelloRequest<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
name_offset: Option<usize>,
}
impl<'a> HelloRequest<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut name_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { name_offset = Some(offset); }
}
Ok(Self {
accessor,
name_offset,
})
}
pub fn name(&self) -> roto_runtime::Result<&'a str> {
let offset = self.name_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn name_or_default(&self) -> roto_runtime::Result<&'a str> {
self.name().or(Ok(""))
}
pub fn has_name(&self) -> bool { self.name_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct HelloRequestBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
name_written: bool,
}
impl<'b> HelloRequestBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> HelloRequestBuilder<'_> {
HelloRequestBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
name_written: false,
}
}
pub fn name(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.name_written = true;
Ok(self)
}
pub fn with(mut self, msg: &HelloRequest<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.name_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedHelloRequest {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedHelloRequest {
type Reader<'a> = HelloRequest<'a>;
fn reader(&self) -> HelloRequest<'_> {
HelloRequest::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedHelloRequest {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedHelloRequest { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct HelloResponse<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
message_offset: Option<usize>,
}
impl<'a> HelloResponse<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut message_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { message_offset = Some(offset); }
}
Ok(Self {
accessor,
message_offset,
})
}
pub fn message(&self) -> roto_runtime::Result<&'a str> {
let offset = self.message_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn message_or_default(&self) -> roto_runtime::Result<&'a str> {
self.message().or(Ok(""))
}
pub fn has_message(&self) -> bool { self.message_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct HelloResponseBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
message_written: bool,
}
impl<'b> HelloResponseBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> HelloResponseBuilder<'_> {
HelloResponseBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
message_written: false,
}
}
pub fn message(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.message_written = true;
Ok(self)
}
pub fn with(mut self, msg: &HelloResponse<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.message_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedHelloResponse {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedHelloResponse {
type Reader<'a> = HelloResponse<'a>;
fn reader(&self) -> HelloResponse<'_> {
HelloResponse::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedHelloResponse {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedHelloResponse { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
#[cfg(feature = "alloc")]
use tonic::{Request, Response, Status};
#[cfg(feature = "alloc")]
use tokio_stream::Stream;
#[cfg(feature = "alloc")]
use std::pin::Pin;
#[cfg(feature = "alloc")]
use std::sync::Arc;
#[cfg(feature = "alloc")]
use std::task::{Context, Poll};
#[cfg(feature = "alloc")]
use std::future::Future;
#[cfg(feature = "alloc")]
use tonic::body::BoxBody;
#[cfg(feature = "alloc")]
use tower::Service;
#[cfg(feature = "alloc")]
use futures_util::StreamExt;
#[cfg(feature = "alloc")]
use http_body_util::BodyExt;
#[cfg(feature = "alloc")]
use http_body::Body;
#[cfg(feature = "alloc")]
use crate::{BufferPool, StatusBody};
#[cfg(feature = "alloc")]
#[async_trait::async_trait]
pub trait HelloWorldService: Send + Sync + 'static {
async fn hello_world(&self, request: Request<OwnedHelloRequest>) -> std::result::Result<Response<OwnedHelloResponse>, Status>;
}
#[cfg(feature = "alloc")]
#[derive(Clone)]
pub struct HelloWorldServiceServer {
inner: Arc<dyn HelloWorldService>,
pool: Arc<BufferPool>,
}
#[cfg(feature = "alloc")]
impl HelloWorldServiceServer {
pub fn new(inner: Arc<dyn HelloWorldService>, pool: Arc<BufferPool>) -> Self {
Self { inner, pool }
}
}
#[cfg(feature = "alloc")]
impl tonic::server::NamedService for HelloWorldServiceServer {
const NAME: &'static str = "hello.HelloWorldService";
}
#[cfg(feature = "alloc")]
impl Service<http::Request<BoxBody>> for HelloWorldServiceServer {
type Response = http::Response<BoxBody>;
type Error = std::convert::Infallible;
type Future = Pin<Box<dyn Future<Output = std::result::Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<std::result::Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<BoxBody>) -> Self::Future {
let inner = self.inner.clone();
let pool = self.pool.clone();
Box::pin(async move {
let path = req.uri().path().to_string();
let body = req.into_body();
let mut buf = pool.get();
let mut stream = body;
while let Some(frame_result) = stream.frame().await {
let frame = frame_result.expect("Body frame error");
if let Some(data) = frame.data_ref() {
buf.put(data.clone());
}
}
let total_len = buf.len();
let bytes_vec = buf.split_to(total_len).freeze();
pool.put(buf);
if bytes_vec.len() < 5 {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
let payload = bytes_vec.slice(5..);
let mut routed = false;
if path == "/hello.HelloWorldService/HelloWorld" {
let request_msg = match OwnedHelloRequest::decode(payload) {
Ok(msg) => msg,
Err(e) => {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
let response = match inner.hello_world(Request::new(request_msg)).await {
Ok(res) => res,
Err(e) => {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
let response_msg = response.into_inner();
let response_bytes = response_msg.bytes();
let mut res_buf = pool.get();
res_buf.put_u8(0);
let len = response_bytes.len() as u32;
res_buf.put_slice(&len.to_be_bytes());
res_buf.put_slice(&response_bytes);
let frame_len = res_buf.len();
let frame = res_buf.split_to(frame_len).freeze();
pool.put(res_buf);
let res_body = BoxBody::new(StatusBody::new(Some(frame), 0));
routed = true;
return Ok(http::Response::builder().status(200).header("content-type", "application/grpc").body(res_body).unwrap());
}
if !routed {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
Ok(http::Response::builder().status(200).body(BoxBody::new(StatusBody::new(None, 0))).unwrap())
})
}
}
+69
View File
@@ -0,0 +1,69 @@
use tonic::Request;
use roto_tonic::RotoCodec;
use hello::{HelloWorldService, OwnedHelloRequest, OwnedHelloResponse};
use roto_runtime::RotoOwned;
use std::task::{Context, Poll};
use tower::Service;
pub use roto_tonic::{BufferPool, StatusBody};
pub mod hello {
include!(concat!(env!("OUT_DIR"), "/hello.rs"));
}
struct ReadyService<S>(S);
impl<S, Req> Service<Req> for ReadyService<S>
where
S: Service<Req>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.0.poll_ready(cx)
}
fn call(&mut self, req: Req) -> S::Future {
let waker = futures_util::task::noop_waker();
let mut cx = std::task::Context::from_waker(&waker);
let _ = self.poll_ready(&mut cx);
self.0.call(req)
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let channel = tonic::transport::Channel::from_static("http://[::1]:50051")
.connect()
.await?;
let ready_channel = ReadyService(channel);
let mut client = tonic::client::Grpc::new(ready_channel);
// We need to specify the method path. For HelloWorldService/HelloWorld, it is "/hello.HelloWorldService/HelloWorld"
let mut buf = vec![0u8; 1024];
let slice = hello::HelloRequestBuilder::builder(&mut buf)
.name("Roto").unwrap()
.finish().unwrap();
let request = OwnedHelloRequest {
data: bytes::Bytes::copy_from_slice(slice),
};
// In tonic's Grpc client, we specify the codec separately.
let response = client
.unary(
Request::new(request),
http::uri::PathAndQuery::from_static("/hello.HelloWorldService/HelloWorld"),
RotoCodec::<OwnedHelloResponse, OwnedHelloRequest>::default(),
)
.await?;
let response_msg: OwnedHelloResponse = response.into_inner();
let reader = response_msg.reader();
println!("Server responded: {}", reader.message().unwrap_or("No message"));
Ok(())
}
+179
View File
@@ -0,0 +1,179 @@
use std::pin::Pin;
use std::future::Future;
use std::task::{Context, Poll};
use std::sync::{Arc, Mutex};
use tonic::{transport::Server, Request, Response, Status};
use roto_tonic::RotoCodec;
use hello::{HelloWorldService, OwnedHelloRequest, OwnedHelloResponse};
use tower::Service;
use bytes::{Bytes, BytesMut, Buf, BufMut};
use tonic::body::BoxBody;
use futures_util::StreamExt;
use roto_runtime::{RotoOwned, RotoMessage};
use http_body_util::BodyExt;
use http_body::Body;
pub use roto_tonic::{BufferPool, StatusBody};
pub mod hello {
include!(concat!(env!("OUT_DIR"), "/hello.rs"));
}
#[derive(Clone)]
pub struct MyHelloWorld {
pool: Arc<BufferPool>,
}
impl MyHelloWorld {
pub fn new(pool: Arc<BufferPool>) -> Self {
Self { pool }
}
}
#[tonic::async_trait]
impl HelloWorldService for MyHelloWorld {
async fn hello_world(
&self,
request: Request<OwnedHelloRequest>,
) -> Result<Response<OwnedHelloResponse>, Status> {
let req = request.into_inner();
let reader = req.reader();
let name = reader.name().unwrap_or("Unknown");
let mut buf = self.pool.get();
buf.resize(1024, 0);
let slice = hello::HelloResponseBuilder::builder(&mut buf[..])
.message(&format!("Hello {}!", name)).unwrap()
.finish().unwrap();
let res_len = slice.len();
let response_bytes = buf.split_to(res_len).freeze();
self.pool.put(buf);
let reply = OwnedHelloResponse {
data: response_bytes,
};
Ok(Response::new(reply))
}
}
// --- Tonic Glue ---
#[derive(Clone)]
pub struct HelloWorldServer {
inner: Arc<MyHelloWorld>,
pool: Arc<BufferPool>,
}
impl HelloWorldServer {
pub fn new(inner: MyHelloWorld, pool: Arc<BufferPool>) -> Self {
Self { inner: Arc::new(inner), pool }
}
}
impl tonic::server::NamedService for HelloWorldServer {
const NAME: &'static str = "hello.HelloWorldService";
}
impl Service<http::Request<BoxBody>> for HelloWorldServer {
type Response = http::Response<BoxBody>;
type Error = std::convert::Infallible;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<BoxBody>) -> Self::Future {
let inner = self.inner.clone();
let pool = self.pool.clone();
println!("Server received request: {} {}", req.method(), req.uri());
Box::pin(async move {
let body = req.into_body();
let mut buf = pool.get();
let mut stream = body;
while let Some(frame_result) = stream.frame().await {
let frame = frame_result.map_err(|e| {
println!("Body frame error: {}", e);
panic!("Body frame error: {}", e);
})?;
if let Some(data) = frame.data_ref() {
buf.put(data.clone());
}
}
let total_len = buf.len();
let bytes_vec = buf.split_to(total_len).freeze();
pool.put(buf);
println!("Collected body bytes: {} bytes", bytes_vec.len());
if bytes_vec.len() < 5 {
println!("Body too short: {} bytes", bytes_vec.len());
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder()
.status(200)
.body(res_body)
.unwrap());
}
println!("Decoding request from {} bytes", bytes_vec.len() - 5);
let request_msg = match OwnedHelloRequest::decode(bytes_vec.slice(5..)) {
Ok(msg) => msg,
Err(e) => {
println!("Decode error: {}", e);
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
println!("Request decoded successfully");
let response = match inner.hello_world(Request::new(request_msg)).await {
Ok(res) => res,
Err(e) => {
println!("Service error: {}", e);
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
let response_msg = response.into_inner();
let response_bytes = response_msg.bytes();
println!("Service responded with {} bytes", response_bytes.len());
let mut res_buf = pool.get();
res_buf.put_u8(0);
let len = response_bytes.len() as u32;
res_buf.put_slice(&len.to_be_bytes());
res_buf.put_slice(&response_bytes);
let frame_len = res_buf.len();
let frame = res_buf.split_to(frame_len).freeze();
pool.put(res_buf);
let res_body = BoxBody::new(StatusBody::new(Some(frame), 0));
Ok(http::Response::builder()
.status(200)
.header("content-type", "application/grpc")
.body(res_body)
.unwrap())
})
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr: std::net::SocketAddr = "[::1]:50051".parse()?;
let pool = Arc::new(BufferPool::new(1024));
let hello = MyHelloWorld::new(pool.clone());
println!("Server listening on {}", addr);
Server::builder()
.add_service(HelloWorldServer::new(hello, pool))
.serve(addr)
.await?;
Ok(())
}
+2
View File
@@ -0,0 +1,2 @@
[build]
target = "thumbv7em-none-eabihf"
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "no_std_test"
version = "0.1.0"
edition = "2021"
[dependencies]
roto-runtime = { path = "../../runtime", default-features = false }
prost = "0.13"
bytes = "1.8"
+805
View File
@@ -0,0 +1,805 @@
// @generated by protoc-gen-roto — do not edit
#[allow(unused_imports)]
use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};
use core::str;
#[cfg(feature = "alloc")]
use bytes::{Bytes, BytesMut, Buf, BufMut};
pub struct Hello<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
name_offset: Option<usize>,
d_offset: Option<usize>,
f_offset: Option<usize>,
b_offset: Option<usize>,
n_offset: Option<usize>,
l_offset: Option<usize>,
c1_offset: Option<usize>,
c2_offset: Option<usize>,
pets_start: Option<usize>,
pets_end: Option<usize>,
}
impl<'a> Hello<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut name_offset = None;
let mut d_offset = None;
let mut f_offset = None;
let mut b_offset = None;
let mut n_offset = None;
let mut l_offset = None;
let mut c1_offset = None;
let mut c2_offset = None;
let mut pets_start = None;
let mut pets_end = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { name_offset = Some(offset); }
if tag.field_number == 2 { d_offset = Some(offset); }
if tag.field_number == 3 { f_offset = Some(offset); }
if tag.field_number == 4 { b_offset = Some(offset); }
if tag.field_number == 5 { n_offset = Some(offset); }
if tag.field_number == 6 { l_offset = Some(offset); }
if tag.field_number == 7 { c1_offset = Some(offset); }
if tag.field_number == 8 { c2_offset = Some(offset); }
if tag.field_number == 9 {
if pets_start.is_none() { pets_start = Some(offset); }
pets_end = Some(offset);
}
}
Ok(Self {
accessor,
name_offset,
d_offset,
f_offset,
b_offset,
n_offset,
l_offset,
c1_offset,
c2_offset,
pets_start, pets_end,
})
}
pub fn name(&self) -> roto_runtime::Result<&'a str> {
let offset = self.name_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn name_or_default(&self) -> roto_runtime::Result<&'a str> {
self.name().or(Ok(""))
}
pub fn has_name(&self) -> bool { self.name_offset.is_some() }
pub fn d(&self) -> roto_runtime::Result<f64> {
let offset = self.d_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(f64::from_le_bytes(bytes.try_into().map_err(|_| roto_runtime::RotoError::WireFormatViolation)?))
}
pub fn d_or_default(&self) -> roto_runtime::Result<f64> {
self.d().or(Ok(0.0))
}
pub fn has_d(&self) -> bool { self.d_offset.is_some() }
pub fn f(&self) -> roto_runtime::Result<f32> {
let offset = self.f_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(f32::from_le_bytes(bytes.try_into().map_err(|_| roto_runtime::RotoError::WireFormatViolation)?))
}
pub fn f_or_default(&self) -> roto_runtime::Result<f32> {
self.f().or(Ok(0.0))
}
pub fn has_f(&self) -> bool { self.f_offset.is_some() }
pub fn b(&self) -> roto_runtime::Result<bool> {
let offset = self.b_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn b_or_default(&self) -> roto_runtime::Result<bool> {
self.b().or(Ok(false))
}
pub fn has_b(&self) -> bool { self.b_offset.is_some() }
pub fn n(&self) -> roto_runtime::Result<i32> {
let offset = self.n_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn n_or_default(&self) -> roto_runtime::Result<i32> {
self.n().or(Ok(0))
}
pub fn has_n(&self) -> bool { self.n_offset.is_some() }
pub fn l(&self) -> roto_runtime::Result<i32> {
let offset = self.l_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn l_or_default(&self) -> roto_runtime::Result<i32> {
self.l().or(Ok(0))
}
pub fn has_l(&self) -> bool { self.l_offset.is_some() }
pub fn c1(&self) -> roto_runtime::Result<&'a str> {
let offset = self.c1_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn c1_or_default(&self) -> roto_runtime::Result<&'a str> {
self.c1().or(Ok(""))
}
pub fn has_c1(&self) -> bool { self.c1_offset.is_some() }
pub fn c2(&self) -> roto_runtime::Result<bool> {
let offset = self.c2_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn c2_or_default(&self) -> roto_runtime::Result<bool> {
self.c2().or(Ok(false))
}
pub fn has_c2(&self) -> bool { self.c2_offset.is_some() }
pub fn pets(&self) -> roto_runtime::RepeatedFieldIterator<'a> {
match (self.pets_start, self.pets_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(9, start, end),
_ => self.accessor.iter_repeated(9),
}
}
pub fn which_choice(&self) -> roto_runtime::Result<Option<hello::Choice<'a>> > {
if self.c1_offset.is_some() {
return Ok(Some(hello::Choice::c1 (self.c1()?)));
}
if self.c2_offset.is_some() {
return Ok(Some(hello::Choice::c2 (self.c2()?)));
}
Ok(None)
}
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct HelloBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
name_written: bool,
d_written: bool,
f_written: bool,
b_written: bool,
n_written: bool,
l_written: bool,
c1_written: bool,
c2_written: bool,
pets_written: bool,
}
impl<'b> HelloBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> HelloBuilder<'_> {
HelloBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
name_written: false,
d_written: false,
f_written: false,
b_written: false,
n_written: false,
l_written: false,
c1_written: false,
c2_written: false,
pets_written: false,
}
}
pub fn name(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.name_written = true;
Ok(self)
}
pub fn d(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(2, value)?;
self.d_written = true;
Ok(self)
}
pub fn f(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(3, value)?;
self.f_written = true;
Ok(self)
}
pub fn b(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(4, value)?;
self.b_written = true;
Ok(self)
}
pub fn n(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(5, value)?;
self.n_written = true;
Ok(self)
}
pub fn l(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(6, value)?;
self.l_written = true;
Ok(self)
}
pub fn c1(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(7, value)?;
self.c1_written = true;
Ok(self)
}
pub fn c2(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(8, value)?;
self.c2_written = true;
Ok(self)
}
pub fn pets(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(9, value)?;
self.pets_written = true;
Ok(self)
}
pub fn with(mut self, msg: &Hello<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.name_written,
2 => self.d_written,
3 => self.f_written,
4 => self.b_written,
5 => self.n_written,
6 => self.l_written,
7 => self.c1_written,
8 => self.c2_written,
9 => self.pets_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedHello {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedHello {
type Reader<'a> = Hello<'a>;
fn reader(&self) -> Hello<'_> {
Hello::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedHello {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedHello { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub mod hello {
pub struct Pet<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
name_offset: Option<usize>,
color_offset: Option<usize>,
}
impl<'a> Pet<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut name_offset = None;
let mut color_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { name_offset = Some(offset); }
if tag.field_number == 2 { color_offset = Some(offset); }
}
Ok(Self {
accessor,
name_offset,
color_offset,
})
}
pub fn name(&self) -> roto_runtime::Result<&'a str> {
let offset = self.name_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn name_or_default(&self) -> roto_runtime::Result<&'a str> {
self.name().or(Ok(""))
}
pub fn has_name(&self) -> bool { self.name_offset.is_some() }
pub fn color(&self) -> roto_runtime::Result<u64> {
let offset = self.color_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as u64).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn color_or_default(&self) -> roto_runtime::Result<u64> {
self.color().or(Ok(0))
}
pub fn has_color(&self) -> bool { self.color_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct PetBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
name_written: bool,
color_written: bool,
}
impl<'b> PetBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> PetBuilder<'_> {
PetBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
name_written: false,
color_written: false,
}
}
pub fn name(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.name_written = true;
Ok(self)
}
pub fn color(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(2, value)?;
self.color_written = true;
Ok(self)
}
pub fn with(mut self, msg: &Pet<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.name_written,
2 => self.color_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedPet {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedPet {
type Reader<'a> = Pet<'a>;
fn reader(&self) -> Pet<'_> {
Pet::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedPet {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedPet { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub mod pet {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum Color {
BLACK = 0,
WHITE = 1,
BLUE = 2,
RED = 3,
YELLOW = 4,
GREEN = 5,
}
impl Color {
pub fn from_i32(value: i32) -> Self {
match value {
0 => Color::BLACK,
1 => Color::WHITE,
2 => Color::BLUE,
3 => Color::RED,
4 => Color::YELLOW,
5 => Color::GREEN,
_ => Color::BLACK,
}
}
}
}
pub enum Choice<'a> {
c1(&'a str),
c2(bool),
}
}
pub struct HelloRequest<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
request_offset: Option<usize>,
}
impl<'a> HelloRequest<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut request_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { request_offset = Some(offset); }
}
Ok(Self {
accessor,
request_offset,
})
}
pub fn request(&self) -> roto_runtime::Result<&'a [u8]> {
let offset = self.request_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes)
}
pub fn request_or_default(&self) -> roto_runtime::Result<&'a [u8]> {
self.request().or(Ok(&[]))
}
pub fn has_request(&self) -> bool { self.request_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct HelloRequestBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
request_written: bool,
}
impl<'b> HelloRequestBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> HelloRequestBuilder<'_> {
HelloRequestBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
request_written: false,
}
}
pub fn request(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(1, value)?;
self.request_written = true;
Ok(self)
}
pub fn with(mut self, msg: &HelloRequest<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.request_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedHelloRequest {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedHelloRequest {
type Reader<'a> = HelloRequest<'a>;
fn reader(&self) -> HelloRequest<'_> {
HelloRequest::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedHelloRequest {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedHelloRequest { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct HelloReply<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
response_offset: Option<usize>,
}
impl<'a> HelloReply<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut response_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { response_offset = Some(offset); }
}
Ok(Self {
accessor,
response_offset,
})
}
pub fn response(&self) -> roto_runtime::Result<&'a [u8]> {
let offset = self.response_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes)
}
pub fn response_or_default(&self) -> roto_runtime::Result<&'a [u8]> {
self.response().or(Ok(&[]))
}
pub fn has_response(&self) -> bool { self.response_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct HelloReplyBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
response_written: bool,
}
impl<'b> HelloReplyBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> HelloReplyBuilder<'_> {
HelloReplyBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
response_written: false,
}
}
pub fn response(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(1, value)?;
self.response_written = true;
Ok(self)
}
pub fn with(mut self, msg: &HelloReply<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.response_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedHelloReply {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedHelloReply {
type Reader<'a> = HelloReply<'a>;
fn reader(&self) -> HelloReply<'_> {
HelloReply::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedHelloReply {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedHelloReply { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
#[cfg(feature = "alloc")]
use tonic::{Request, Response, Status};
#[cfg(feature = "alloc")]
use tokio_stream::Stream;
#[cfg(feature = "alloc")]
use std::pin::Pin;
#[cfg(feature = "alloc")]
use std::sync::Arc;
#[cfg(feature = "alloc")]
use std::task::{Context, Poll};
#[cfg(feature = "alloc")]
use std::future::Future;
#[cfg(feature = "alloc")]
use tonic::body::BoxBody;
#[cfg(feature = "alloc")]
use tower::Service;
#[cfg(feature = "alloc")]
use futures_util::StreamExt;
#[cfg(feature = "alloc")]
use http_body_util::BodyExt;
#[cfg(feature = "alloc")]
use http_body::Body;
#[cfg(feature = "alloc")]
use crate::{BufferPool, StatusBody};
#[cfg(feature = "alloc")]
#[async_trait::async_trait]
pub trait Greeter: Send + Sync + 'static {
async fn say_hello(&self, request: Request<OwnedHelloRequest>) -> std::result::Result<Response<OwnedHelloReply>, Status>;
}
#[cfg(feature = "alloc")]
#[derive(Clone)]
pub struct GreeterServer {
inner: Arc<dyn Greeter>,
pool: Arc<BufferPool>,
}
#[cfg(feature = "alloc")]
impl GreeterServer {
pub fn new(inner: Arc<dyn Greeter>, pool: Arc<BufferPool>) -> Self {
Self { inner, pool }
}
}
#[cfg(feature = "alloc")]
impl tonic::server::NamedService for GreeterServer {
const NAME: &'static str = "helloworld.Greeter";
}
#[cfg(feature = "alloc")]
impl Service<http::Request<BoxBody>> for GreeterServer {
type Response = http::Response<BoxBody>;
type Error = std::convert::Infallible;
type Future = Pin<Box<dyn Future<Output = std::result::Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<std::result::Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<BoxBody>) -> Self::Future {
let inner = self.inner.clone();
let pool = self.pool.clone();
Box::pin(async move {
let path = req.uri().path().to_string();
let body = req.into_body();
let mut buf = pool.get();
let mut stream = body;
while let Some(frame_result) = stream.frame().await {
let frame = frame_result.expect("Body frame error");
if let Some(data) = frame.data_ref() {
buf.put(data.clone());
}
}
let total_len = buf.len();
let bytes_vec = buf.split_to(total_len).freeze();
pool.put(buf);
if bytes_vec.len() < 5 {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
let payload = bytes_vec.slice(5..);
let mut routed = false;
if path == "/helloworld.Greeter/SayHello" {
let request_msg = match OwnedHelloRequest::decode(payload) {
Ok(msg) => msg,
Err(e) => {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
let response = match inner.say_hello(Request::new(request_msg)).await {
Ok(res) => res,
Err(e) => {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
let response_msg = response.into_inner();
let response_bytes = response_msg.bytes();
let mut res_buf = pool.get();
res_buf.put_u8(0);
let len = response_bytes.len() as u32;
res_buf.put_slice(&len.to_be_bytes());
res_buf.put_slice(&response_bytes);
let frame_len = res_buf.len();
let frame = res_buf.split_to(frame_len).freeze();
pool.put(res_buf);
let res_body = BoxBody::new(StatusBody::new(Some(frame), 0));
routed = true;
return Ok(http::Response::builder().status(200).header("content-type", "application/grpc").body(res_body).unwrap());
}
if !routed {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
Ok(http::Response::builder().status(200).body(BoxBody::new(StatusBody::new(None, 0))).unwrap())
})
}
}
+14
View File
@@ -0,0 +1,14 @@
#![no_std]
#![no_main]
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[no_mangle]
pub extern "C" fn _start() -> ! {
loop {}
}
Submodule
+1
Submodule grpc_bench added at c645de5855
BIN
View File
Binary file not shown.
+773
View File
@@ -0,0 +1,773 @@
// @generated by protoc-gen-roto — do not edit
#[allow(unused_imports)]
use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};
use std::str;
use bytes::{Bytes, BytesMut, Buf, BufMut};
use tonic::{Request, Response, Status};
use tokio_stream::Stream;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use std::future::Future;
use tonic::body::BoxBody;
use tower::Service;
use futures_util::StreamExt;
use http_body_util::BodyExt;
use http_body::Body;
use roto_tonic::{BufferPool, StatusBody};
pub struct Hello<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
name_offset: Option<usize>,
d_offset: Option<usize>,
f_offset: Option<usize>,
b_offset: Option<usize>,
n_offset: Option<usize>,
l_offset: Option<usize>,
c1_offset: Option<usize>,
c2_offset: Option<usize>,
pets_start: Option<usize>,
pets_end: Option<usize>,
}
impl<'a> Hello<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut name_offset = None;
let mut d_offset = None;
let mut f_offset = None;
let mut b_offset = None;
let mut n_offset = None;
let mut l_offset = None;
let mut c1_offset = None;
let mut c2_offset = None;
let mut pets_start = None;
let mut pets_end = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { name_offset = Some(offset); }
if tag.field_number == 2 { d_offset = Some(offset); }
if tag.field_number == 3 { f_offset = Some(offset); }
if tag.field_number == 4 { b_offset = Some(offset); }
if tag.field_number == 5 { n_offset = Some(offset); }
if tag.field_number == 6 { l_offset = Some(offset); }
if tag.field_number == 7 { c1_offset = Some(offset); }
if tag.field_number == 8 { c2_offset = Some(offset); }
if tag.field_number == 9 {
if pets_start.is_none() { pets_start = Some(offset); }
pets_end = Some(offset);
}
}
Ok(Self {
accessor,
name_offset,
d_offset,
f_offset,
b_offset,
n_offset,
l_offset,
c1_offset,
c2_offset,
pets_start, pets_end,
})
}
pub fn name(&self) -> roto_runtime::Result<&'a str> {
let offset = self.name_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
std::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn name_or_default(&self) -> roto_runtime::Result<&'a str> {
self.name().or(Ok(""))
}
pub fn has_name(&self) -> bool { self.name_offset.is_some() }
pub fn d(&self) -> roto_runtime::Result<f64> {
let offset = self.d_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(f64::from_le_bytes(bytes.try_into().map_err(|_| roto_runtime::RotoError::WireFormatViolation)?))
}
pub fn d_or_default(&self) -> roto_runtime::Result<f64> {
self.d().or(Ok(0.0))
}
pub fn has_d(&self) -> bool { self.d_offset.is_some() }
pub fn f(&self) -> roto_runtime::Result<f32> {
let offset = self.f_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(f32::from_le_bytes(bytes.try_into().map_err(|_| roto_runtime::RotoError::WireFormatViolation)?))
}
pub fn f_or_default(&self) -> roto_runtime::Result<f32> {
self.f().or(Ok(0.0))
}
pub fn has_f(&self) -> bool { self.f_offset.is_some() }
pub fn b(&self) -> roto_runtime::Result<bool> {
let offset = self.b_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn b_or_default(&self) -> roto_runtime::Result<bool> {
self.b().or(Ok(false))
}
pub fn has_b(&self) -> bool { self.b_offset.is_some() }
pub fn n(&self) -> roto_runtime::Result<i32> {
let offset = self.n_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn n_or_default(&self) -> roto_runtime::Result<i32> {
self.n().or(Ok(0))
}
pub fn has_n(&self) -> bool { self.n_offset.is_some() }
pub fn l(&self) -> roto_runtime::Result<i32> {
let offset = self.l_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn l_or_default(&self) -> roto_runtime::Result<i32> {
self.l().or(Ok(0))
}
pub fn has_l(&self) -> bool { self.l_offset.is_some() }
pub fn c1(&self) -> roto_runtime::Result<&'a str> {
let offset = self.c1_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
std::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn c1_or_default(&self) -> roto_runtime::Result<&'a str> {
self.c1().or(Ok(""))
}
pub fn has_c1(&self) -> bool { self.c1_offset.is_some() }
pub fn c2(&self) -> roto_runtime::Result<bool> {
let offset = self.c2_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn c2_or_default(&self) -> roto_runtime::Result<bool> {
self.c2().or(Ok(false))
}
pub fn has_c2(&self) -> bool { self.c2_offset.is_some() }
pub fn pets(&self) -> roto_runtime::RepeatedFieldIterator<'a> {
match (self.pets_start, self.pets_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(9, start, end),
_ => self.accessor.iter_repeated(9),
}
}
pub fn which_choice(&self) -> roto_runtime::Result<Option<hello::Choice<'a>> > {
if self.c1_offset.is_some() {
return Ok(Some(hello::Choice::c1 (self.c1()?)));
}
if self.c2_offset.is_some() {
return Ok(Some(hello::Choice::c2 (self.c2()?)));
}
Ok(None)
}
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct HelloBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
name_written: bool,
d_written: bool,
f_written: bool,
b_written: bool,
n_written: bool,
l_written: bool,
c1_written: bool,
c2_written: bool,
pets_written: bool,
}
impl<'b> HelloBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> HelloBuilder<'_> {
HelloBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
name_written: false,
d_written: false,
f_written: false,
b_written: false,
n_written: false,
l_written: false,
c1_written: false,
c2_written: false,
pets_written: false,
}
}
pub fn name(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.name_written = true;
Ok(self)
}
pub fn d(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(2, value)?;
self.d_written = true;
Ok(self)
}
pub fn f(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(3, value)?;
self.f_written = true;
Ok(self)
}
pub fn b(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(4, value)?;
self.b_written = true;
Ok(self)
}
pub fn n(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(5, value)?;
self.n_written = true;
Ok(self)
}
pub fn l(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(6, value)?;
self.l_written = true;
Ok(self)
}
pub fn c1(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(7, value)?;
self.c1_written = true;
Ok(self)
}
pub fn c2(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(8, value)?;
self.c2_written = true;
Ok(self)
}
pub fn pets(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(9, value)?;
self.pets_written = true;
Ok(self)
}
pub fn with(mut self, msg: &Hello<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.name_written,
2 => self.d_written,
3 => self.f_written,
4 => self.b_written,
5 => self.n_written,
6 => self.l_written,
7 => self.c1_written,
8 => self.c2_written,
9 => self.pets_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct OwnedHello {
pub data: bytes::Bytes,
}
impl roto_runtime::RotoOwned for OwnedHello {
type Reader<'a> = Hello<'a>;
fn reader(&self) -> Hello<'_> {
Hello::new(&self.data).expect("failed to create reader")
}
}
impl roto_runtime::RotoMessage for OwnedHello {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedHello { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub mod hello {
pub struct Pet<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
name_offset: Option<usize>,
color_offset: Option<usize>,
}
impl<'a> Pet<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut name_offset = None;
let mut color_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { name_offset = Some(offset); }
if tag.field_number == 2 { color_offset = Some(offset); }
}
Ok(Self {
accessor,
name_offset,
color_offset,
})
}
pub fn name(&self) -> roto_runtime::Result<&'a str> {
let offset = self.name_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
std::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn name_or_default(&self) -> roto_runtime::Result<&'a str> {
self.name().or(Ok(""))
}
pub fn has_name(&self) -> bool { self.name_offset.is_some() }
pub fn color(&self) -> roto_runtime::Result<u64> {
let offset = self.color_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as u64).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn color_or_default(&self) -> roto_runtime::Result<u64> {
self.color().or(Ok(0))
}
pub fn has_color(&self) -> bool { self.color_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct PetBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
name_written: bool,
color_written: bool,
}
impl<'b> PetBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> PetBuilder<'_> {
PetBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
name_written: false,
color_written: false,
}
}
pub fn name(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.name_written = true;
Ok(self)
}
pub fn color(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(2, value)?;
self.color_written = true;
Ok(self)
}
pub fn with(mut self, msg: &Pet<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.name_written,
2 => self.color_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct OwnedPet {
pub data: bytes::Bytes,
}
impl roto_runtime::RotoOwned for OwnedPet {
type Reader<'a> = Pet<'a>;
fn reader(&self) -> Pet<'_> {
Pet::new(&self.data).expect("failed to create reader")
}
}
impl roto_runtime::RotoMessage for OwnedPet {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedPet { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub mod pet {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum Color {
BLACK = 0,
WHITE = 1,
BLUE = 2,
RED = 3,
YELLOW = 4,
GREEN = 5,
}
impl Color {
pub fn from_i32(value: i32) -> Self {
match value {
0 => Color::BLACK,
1 => Color::WHITE,
2 => Color::BLUE,
3 => Color::RED,
4 => Color::YELLOW,
5 => Color::GREEN,
_ => Color::BLACK,
}
}
}
}
pub enum Choice<'a> {
c1(&'a str),
c2(bool),
}
}
pub struct HelloRequest<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
request_offset: Option<usize>,
}
impl<'a> HelloRequest<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut request_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { request_offset = Some(offset); }
}
Ok(Self {
accessor,
request_offset,
})
}
pub fn request(&self) -> roto_runtime::Result<&'a [u8]> {
let offset = self.request_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes)
}
pub fn request_or_default(&self) -> roto_runtime::Result<&'a [u8]> {
self.request().or(Ok(&[]))
}
pub fn has_request(&self) -> bool { self.request_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct HelloRequestBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
request_written: bool,
}
impl<'b> HelloRequestBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> HelloRequestBuilder<'_> {
HelloRequestBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
request_written: false,
}
}
pub fn request(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(1, value)?;
self.request_written = true;
Ok(self)
}
pub fn with(mut self, msg: &HelloRequest<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.request_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct OwnedHelloRequest {
pub data: bytes::Bytes,
}
impl roto_runtime::RotoOwned for OwnedHelloRequest {
type Reader<'a> = HelloRequest<'a>;
fn reader(&self) -> HelloRequest<'_> {
HelloRequest::new(&self.data).expect("failed to create reader")
}
}
impl roto_runtime::RotoMessage for OwnedHelloRequest {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedHelloRequest { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct HelloReply<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
response_offset: Option<usize>,
}
impl<'a> HelloReply<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut response_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { response_offset = Some(offset); }
}
Ok(Self {
accessor,
response_offset,
})
}
pub fn response(&self) -> roto_runtime::Result<&'a [u8]> {
let offset = self.response_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes)
}
pub fn response_or_default(&self) -> roto_runtime::Result<&'a [u8]> {
self.response().or(Ok(&[]))
}
pub fn has_response(&self) -> bool { self.response_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct HelloReplyBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
response_written: bool,
}
impl<'b> HelloReplyBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> HelloReplyBuilder<'_> {
HelloReplyBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
response_written: false,
}
}
pub fn response(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(1, value)?;
self.response_written = true;
Ok(self)
}
pub fn with(mut self, msg: &HelloReply<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.response_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct OwnedHelloReply {
pub data: bytes::Bytes,
}
impl roto_runtime::RotoOwned for OwnedHelloReply {
type Reader<'a> = HelloReply<'a>;
fn reader(&self) -> HelloReply<'_> {
HelloReply::new(&self.data).expect("failed to create reader")
}
}
impl roto_runtime::RotoMessage for OwnedHelloReply {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedHelloReply { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
#[tonic::async_trait]
pub trait Greeter: Send + Sync + 'static {
async fn say_hello(&self, request: Request<OwnedHelloRequest>) -> std::result::Result<Response<OwnedHelloReply>, Status>;
}
pub struct GreeterServer {
inner: Arc<dyn Greeter>,
pool: Arc<BufferPool>,
}
impl GreeterServer {
pub fn new(inner: Arc<dyn Greeter>, pool: Arc<BufferPool>) -> Self {
Self { inner, pool }
}
}
impl tonic::server::NamedService for GreeterServer {
const NAME: &'static str = "Greeter";
}
impl Service<http::Request<BoxBody>> for GreeterServer {
type Response = http::Response<BoxBody>;
type Error = std::convert::Infallible;
type Future = Pin<Box<dyn Future<Output = std::result::Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<std::result::Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<BoxBody>) -> Self::Future {
let inner = self.inner.clone();
let pool = self.pool.clone();
Box::pin(async move {
let path = req.uri().path().to_string();
let body = req.into_body();
let mut buf = pool.get();
let mut stream = body;
while let Some(frame_result) = stream.frame().await {
let frame = frame_result.expect("Body frame error");
if let Some(data) = frame.data_ref() {
buf.put(data.clone());
}
}
let total_len = buf.len();
let bytes_vec = buf.split_to(total_len).freeze();
pool.put(buf);
if bytes_vec.len() < 5 {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
let payload = bytes_vec.slice(5..);
let mut routed = false;
if path == "/Greeter/say_hello" {
let request_msg = match OwnedHelloRequest::decode(payload) {
Ok(msg) => msg,
Err(e) => {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
let response = match inner.say_hello(Request::new(request_msg)).await {
Ok(res) => res,
Err(e) => {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
let response_msg = response.into_inner();
let response_bytes = response_msg.bytes();
let mut res_buf = pool.get();
res_buf.put_u8(0);
let len = response_bytes.len() as u32;
res_buf.put_slice(&len.to_be_bytes());
res_buf.put_slice(&response_bytes);
let frame_len = res_buf.len();
let frame = res_buf.split_to(frame_len).freeze();
pool.put(res_buf);
let res_body = BoxBody::new(StatusBody::new(Some(frame), 0));
routed = true;
return Ok(http::Response::builder().status(200).header("content-type", "application/grpc").body(res_body).unwrap());
}
if !routed {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
Ok(http::Response::builder().status(200).body(BoxBody::new(StatusBody::new(None, 0))).unwrap())
})
}
}
+12
View File
@@ -0,0 +1,12 @@
You are the Codemonkey. Your role is the pure implementation of technical tasks.
Your workflow:
1. Review the implementation plan in the `artifacts/` directory.
2. Implement the changes in the codebase following the provided plan and established coding standards.
3. After every significant change, you MUST:
- Ensure the codebase compiles without errors.
- Run all relevant tests to ensure no regressions were introduced.
- If tests fail, fix the issues before proceeding.
4. Document your progress and the final outcome in a verification report in the `artifacts/` directory (e.g., `artifacts/verification_report.md`). This report should list the tasks completed and provide evidence (e.g., test output) that the solution works as intended.
5. Do not deviate from the plan without consulting the Orchestrator.
6. Once the tasks are complete and verified, notify the Orchestrator that the codebase is updated and the verification report artifact is ready.
+9
View File
@@ -0,0 +1,9 @@
You are the Coordinator. Your role is to transform high-level problem statements and research data into a concrete, executable implementation plan.
Your workflow:
1. Review the problem statement and the `artifacts/research_report.md` produced by the Research agent.
2. Break down the overall goal into a sequence of small, manageable, and independent tasks.
3. For each task, specify the expected outcome and any dependencies on previous tasks.
4. Ensure the plan is logically ordered and covers all edge cases identified during research.
5. Save the final plan as `artifacts/implementation_plan.md` for human review and as a guide for the Codemonkey.
6. Notify the Orchestrator that the implementation plan artifact is ready.
+14
View File
@@ -0,0 +1,14 @@
You are the Orchestrator. Your role is to manage the end-to-end resolution of a complex technical problem. Make extensive use of the
subagent tool.
Your workflow:
1. Analyze the initial problem statement.
2. Delegate research to the Research agent. Ensure the Research agent produces a `artifacts/research_report.md` for human review.
3. Coordinate with the Coordinator agent to break down the findings into a detailed plan. Ensure the Coordinator produces a `artifacts/implementation_plan.md` for human review.
4. Delegate the implementation of these steps to the Codemonkey agent. Ensure the Codemonkey produces a `artifacts/verification_report.md` upon completion.
5. Review the artifacts in the `artifacts/` directory and the results from the Codemonkey agent, ensuring all requirements are met and the solution is correct.
6. If issues arise, loop back to the Research or Coordinator agents as needed, updating the relevant artifacts.
Your goal is to act as the central hub of communication and decision-making, ensuring a systematic and verified approach to the problem.
You must tell subagents what persona they are, and provide them the path of the persona file they should read.
+13
View File
@@ -0,0 +1,13 @@
You are the Researcher. Your role is to conduct a comprehensive initial analysis of a problem to identify the best technical approach.
Your workflow:
1. Analyze the problem statement to identify key technical requirements and unknown areas.
2. Use available tools (such as SearXNG and fetch) to research existing libraries, frameworks, APIs, and best practices relevant to the problem.
3. Explore the current codebase to understand how the new functionality fits in or what existing patterns should be followed.
4. Compile a detailed report including:
- Recommended tools and libraries.
- Potential challenges or pitfalls.
- Suggested architectural approach.
- Relevant documentation links.
5. Save this report as `artifacts/research_report.md` for human review.
6. Notify the Orchestrator that the research report artifact is ready.
+67
View File
@@ -0,0 +1,67 @@
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
option go_package = "proto/helloworld";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The actual message exchanged by the client and the server.
// NOTE: When creating a custom scenario plese edit only this message.
message Hello {
string name = 1;
double d = 2;
float f = 3;
bool b = 4;
int32 n = 5;
int64 l = 6;
oneof choice {
string c1 = 7;
bool c2 = 8;
}
message Pet {
enum Color {
BLACK = 0;
WHITE = 1;
BLUE = 2;
RED = 3;
YELLOW = 4;
GREEN = 5;
}
string name = 1;
Color color = 2;
}
repeated Pet pets = 9;
}
// The request message from the client.
message HelloRequest {
Hello request = 1;
}
// The response message from the server.
message HelloReply {
Hello response = 1;
}
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "roto-tonic"
version = "0.1.0"
edition = "2024"
[dependencies]
roto-runtime = { path = "../runtime" }
tonic = "0.12"
bytes = "1.7"
prost = "0.13"
http-body = "1.0"
http-body-util = "0.1"
tower = "0.4"
futures-util = "0.3"
async-trait = "0.1"
tokio-stream = { version = "0.1", features = ["net"] }
tokio = { version = "1.38", features = ["full"] }
http = "1.1"
[build-dependencies]
tonic-build = "0.12"
[features]
default = ["alloc"]
alloc = []
+36
View File
@@ -0,0 +1,36 @@
use std::env;
use std::process::Command;
use std::path::PathBuf;
fn main() {
let proto_file = "proto/interop.proto";
// 1. Generate prost/tonic code
tonic_build::compile_protos(proto_file).expect("Failed to compile protos with tonic-build");
// 2. Generate roto code
// Find protoc-gen-roto
// We assume it's in the target/debug folder of the root project
let root_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let plugin_path = PathBuf::from(&root_dir)
.join("../")
.join("target/debug/protoc-gen-roto");
if !plugin_path.exists() {
println!("cargo:warning=protoc-gen-roto plugin not found at {:?}. Roto code generation will be skipped.", plugin_path);
return;
}
let out_dir = PathBuf::from(&root_dir).join("src/generated");
let status = Command::new("protoc")
.arg(format!("--plugin=protoc-gen-roto={}", plugin_path.to_str().unwrap()))
.arg(format!("--roto_out={}", out_dir.to_str().unwrap()))
.arg(proto_file)
.status()
.expect("Failed to execute protoc");
if !status.success() {
panic!("protoc failed to generate roto code");
}
}
+26
View File
@@ -0,0 +1,26 @@
syntax = "proto3";
package interop;
service InteropService {
// Expected to succeed
rpc UnaryCall (UnaryRequest) returns (UnaryResponse);
// Expected to fail (roto does not support streaming)
rpc StreamingCall (StreamingRequest) returns (stream StreamingResponse);
}
message UnaryRequest {
string message = 1;
}
message UnaryResponse {
string reply = 1;
}
message StreamingRequest {
string query = 1;
}
message StreamingResponse {
string item = 1;
}
+774
View File
@@ -0,0 +1,774 @@
// @generated by protoc-gen-roto — do not edit
#[allow(unused_imports)]
use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};
use std::str;
use bytes::{Bytes, BytesMut, Buf, BufMut};
use tonic::{Request, Response, Status};
use tokio_stream::Stream;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use std::future::Future;
use tonic::body::BoxBody;
use tower::Service;
use futures_util::StreamExt;
use http_body_util::BodyExt;
use http_body::Body;
use crate::{BufferPool, StatusBody};
pub struct Hello<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
name_offset: Option<usize>,
d_offset: Option<usize>,
f_offset: Option<usize>,
b_offset: Option<usize>,
n_offset: Option<usize>,
l_offset: Option<usize>,
c1_offset: Option<usize>,
c2_offset: Option<usize>,
pets_start: Option<usize>,
pets_end: Option<usize>,
}
impl<'a> Hello<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut name_offset = None;
let mut d_offset = None;
let mut f_offset = None;
let mut b_offset = None;
let mut n_offset = None;
let mut l_offset = None;
let mut c1_offset = None;
let mut c2_offset = None;
let mut pets_start = None;
let mut pets_end = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { name_offset = Some(offset); }
if tag.field_number == 2 { d_offset = Some(offset); }
if tag.field_number == 3 { f_offset = Some(offset); }
if tag.field_number == 4 { b_offset = Some(offset); }
if tag.field_number == 5 { n_offset = Some(offset); }
if tag.field_number == 6 { l_offset = Some(offset); }
if tag.field_number == 7 { c1_offset = Some(offset); }
if tag.field_number == 8 { c2_offset = Some(offset); }
if tag.field_number == 9 {
if pets_start.is_none() { pets_start = Some(offset); }
pets_end = Some(offset);
}
}
Ok(Self {
accessor,
name_offset,
d_offset,
f_offset,
b_offset,
n_offset,
l_offset,
c1_offset,
c2_offset,
pets_start, pets_end,
})
}
pub fn name(&self) -> roto_runtime::Result<&'a str> {
let offset = self.name_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
std::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn name_or_default(&self) -> roto_runtime::Result<&'a str> {
self.name().or(Ok(""))
}
pub fn has_name(&self) -> bool { self.name_offset.is_some() }
pub fn d(&self) -> roto_runtime::Result<f64> {
let offset = self.d_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(f64::from_le_bytes(bytes.try_into().map_err(|_| roto_runtime::RotoError::WireFormatViolation)?))
}
pub fn d_or_default(&self) -> roto_runtime::Result<f64> {
self.d().or(Ok(0.0))
}
pub fn has_d(&self) -> bool { self.d_offset.is_some() }
pub fn f(&self) -> roto_runtime::Result<f32> {
let offset = self.f_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(f32::from_le_bytes(bytes.try_into().map_err(|_| roto_runtime::RotoError::WireFormatViolation)?))
}
pub fn f_or_default(&self) -> roto_runtime::Result<f32> {
self.f().or(Ok(0.0))
}
pub fn has_f(&self) -> bool { self.f_offset.is_some() }
pub fn b(&self) -> roto_runtime::Result<bool> {
let offset = self.b_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn b_or_default(&self) -> roto_runtime::Result<bool> {
self.b().or(Ok(false))
}
pub fn has_b(&self) -> bool { self.b_offset.is_some() }
pub fn n(&self) -> roto_runtime::Result<i32> {
let offset = self.n_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn n_or_default(&self) -> roto_runtime::Result<i32> {
self.n().or(Ok(0))
}
pub fn has_n(&self) -> bool { self.n_offset.is_some() }
pub fn l(&self) -> roto_runtime::Result<i32> {
let offset = self.l_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn l_or_default(&self) -> roto_runtime::Result<i32> {
self.l().or(Ok(0))
}
pub fn has_l(&self) -> bool { self.l_offset.is_some() }
pub fn c1(&self) -> roto_runtime::Result<&'a str> {
let offset = self.c1_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
std::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn c1_or_default(&self) -> roto_runtime::Result<&'a str> {
self.c1().or(Ok(""))
}
pub fn has_c1(&self) -> bool { self.c1_offset.is_some() }
pub fn c2(&self) -> roto_runtime::Result<bool> {
let offset = self.c2_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn c2_or_default(&self) -> roto_runtime::Result<bool> {
self.c2().or(Ok(false))
}
pub fn has_c2(&self) -> bool { self.c2_offset.is_some() }
pub fn pets(&self) -> roto_runtime::RepeatedFieldIterator<'a> {
match (self.pets_start, self.pets_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(9, start, end),
_ => self.accessor.iter_repeated(9),
}
}
pub fn which_choice(&self) -> roto_runtime::Result<Option<hello::Choice<'a>> > {
if self.c1_offset.is_some() {
return Ok(Some(hello::Choice::c1 (self.c1()?)));
}
if self.c2_offset.is_some() {
return Ok(Some(hello::Choice::c2 (self.c2()?)));
}
Ok(None)
}
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct HelloBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
name_written: bool,
d_written: bool,
f_written: bool,
b_written: bool,
n_written: bool,
l_written: bool,
c1_written: bool,
c2_written: bool,
pets_written: bool,
}
impl<'b> HelloBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> HelloBuilder<'_> {
HelloBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
name_written: false,
d_written: false,
f_written: false,
b_written: false,
n_written: false,
l_written: false,
c1_written: false,
c2_written: false,
pets_written: false,
}
}
pub fn name(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.name_written = true;
Ok(self)
}
pub fn d(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(2, value)?;
self.d_written = true;
Ok(self)
}
pub fn f(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(3, value)?;
self.f_written = true;
Ok(self)
}
pub fn b(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(4, value)?;
self.b_written = true;
Ok(self)
}
pub fn n(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(5, value)?;
self.n_written = true;
Ok(self)
}
pub fn l(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(6, value)?;
self.l_written = true;
Ok(self)
}
pub fn c1(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(7, value)?;
self.c1_written = true;
Ok(self)
}
pub fn c2(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(8, value)?;
self.c2_written = true;
Ok(self)
}
pub fn pets(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(9, value)?;
self.pets_written = true;
Ok(self)
}
pub fn with(mut self, msg: &Hello<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.name_written,
2 => self.d_written,
3 => self.f_written,
4 => self.b_written,
5 => self.n_written,
6 => self.l_written,
7 => self.c1_written,
8 => self.c2_written,
9 => self.pets_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct OwnedHello {
pub data: bytes::Bytes,
}
impl roto_runtime::RotoOwned for OwnedHello {
type Reader<'a> = Hello<'a>;
fn reader(&self) -> Hello<'_> {
Hello::new(&self.data).expect("failed to create reader")
}
}
impl roto_runtime::RotoMessage for OwnedHello {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedHello { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub mod hello {
pub struct Pet<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
name_offset: Option<usize>,
color_offset: Option<usize>,
}
impl<'a> Pet<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut name_offset = None;
let mut color_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { name_offset = Some(offset); }
if tag.field_number == 2 { color_offset = Some(offset); }
}
Ok(Self {
accessor,
name_offset,
color_offset,
})
}
pub fn name(&self) -> roto_runtime::Result<&'a str> {
let offset = self.name_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
std::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn name_or_default(&self) -> roto_runtime::Result<&'a str> {
self.name().or(Ok(""))
}
pub fn has_name(&self) -> bool { self.name_offset.is_some() }
pub fn color(&self) -> roto_runtime::Result<u64> {
let offset = self.color_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as u64).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn color_or_default(&self) -> roto_runtime::Result<u64> {
self.color().or(Ok(0))
}
pub fn has_color(&self) -> bool { self.color_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct PetBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
name_written: bool,
color_written: bool,
}
impl<'b> PetBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> PetBuilder<'_> {
PetBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
name_written: false,
color_written: false,
}
}
pub fn name(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.name_written = true;
Ok(self)
}
pub fn color(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(2, value)?;
self.color_written = true;
Ok(self)
}
pub fn with(mut self, msg: &Pet<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.name_written,
2 => self.color_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct OwnedPet {
pub data: bytes::Bytes,
}
impl roto_runtime::RotoOwned for OwnedPet {
type Reader<'a> = Pet<'a>;
fn reader(&self) -> Pet<'_> {
Pet::new(&self.data).expect("failed to create reader")
}
}
impl roto_runtime::RotoMessage for OwnedPet {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedPet { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub mod pet {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum Color {
BLACK = 0,
WHITE = 1,
BLUE = 2,
RED = 3,
YELLOW = 4,
GREEN = 5,
}
impl Color {
pub fn from_i32(value: i32) -> Self {
match value {
0 => Color::BLACK,
1 => Color::WHITE,
2 => Color::BLUE,
3 => Color::RED,
4 => Color::YELLOW,
5 => Color::GREEN,
_ => Color::BLACK,
}
}
}
}
pub enum Choice<'a> {
c1(&'a str),
c2(bool),
}
}
pub struct HelloRequest<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
request_offset: Option<usize>,
}
impl<'a> HelloRequest<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut request_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { request_offset = Some(offset); }
}
Ok(Self {
accessor,
request_offset,
})
}
pub fn request(&self) -> roto_runtime::Result<&'a [u8]> {
let offset = self.request_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes)
}
pub fn request_or_default(&self) -> roto_runtime::Result<&'a [u8]> {
self.request().or(Ok(&[]))
}
pub fn has_request(&self) -> bool { self.request_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct HelloRequestBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
request_written: bool,
}
impl<'b> HelloRequestBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> HelloRequestBuilder<'_> {
HelloRequestBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
request_written: false,
}
}
pub fn request(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(1, value)?;
self.request_written = true;
Ok(self)
}
pub fn with(mut self, msg: &HelloRequest<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.request_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct OwnedHelloRequest {
pub data: bytes::Bytes,
}
impl roto_runtime::RotoOwned for OwnedHelloRequest {
type Reader<'a> = HelloRequest<'a>;
fn reader(&self) -> HelloRequest<'_> {
HelloRequest::new(&self.data).expect("failed to create reader")
}
}
impl roto_runtime::RotoMessage for OwnedHelloRequest {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedHelloRequest { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct HelloReply<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
response_offset: Option<usize>,
}
impl<'a> HelloReply<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut response_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { response_offset = Some(offset); }
}
Ok(Self {
accessor,
response_offset,
})
}
pub fn response(&self) -> roto_runtime::Result<&'a [u8]> {
let offset = self.response_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes)
}
pub fn response_or_default(&self) -> roto_runtime::Result<&'a [u8]> {
self.response().or(Ok(&[]))
}
pub fn has_response(&self) -> bool { self.response_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct HelloReplyBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
response_written: bool,
}
impl<'b> HelloReplyBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> HelloReplyBuilder<'_> {
HelloReplyBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
response_written: false,
}
}
pub fn response(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(1, value)?;
self.response_written = true;
Ok(self)
}
pub fn with(mut self, msg: &HelloReply<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.response_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct OwnedHelloReply {
pub data: bytes::Bytes,
}
impl roto_runtime::RotoOwned for OwnedHelloReply {
type Reader<'a> = HelloReply<'a>;
fn reader(&self) -> HelloReply<'_> {
HelloReply::new(&self.data).expect("failed to create reader")
}
}
impl roto_runtime::RotoMessage for OwnedHelloReply {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedHelloReply { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
#[tonic::async_trait]
pub trait Greeter: Send + Sync + 'static {
async fn say_hello(&self, request: Request<OwnedHelloRequest>) -> std::result::Result<Response<OwnedHelloReply>, Status>;
}
#[derive(Clone)]
pub struct GreeterServer {
inner: Arc<dyn Greeter>,
pool: Arc<BufferPool>,
}
impl GreeterServer {
pub fn new(inner: Arc<dyn Greeter>, pool: Arc<BufferPool>) -> Self {
Self { inner, pool }
}
}
impl tonic::server::NamedService for GreeterServer {
const NAME: &'static str = "helloworld.Greeter";
}
impl Service<http::Request<BoxBody>> for GreeterServer {
type Response = http::Response<BoxBody>;
type Error = std::convert::Infallible;
type Future = Pin<Box<dyn Future<Output = std::result::Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<std::result::Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<BoxBody>) -> Self::Future {
let inner = self.inner.clone();
let pool = self.pool.clone();
Box::pin(async move {
let path = req.uri().path().to_string();
let body = req.into_body();
let mut buf = pool.get();
let mut stream = body;
while let Some(frame_result) = stream.frame().await {
let frame = frame_result.expect("Body frame error");
if let Some(data) = frame.data_ref() {
buf.put(data.clone());
}
}
let total_len = buf.len();
let bytes_vec = buf.split_to(total_len).freeze();
pool.put(buf);
if bytes_vec.len() < 5 {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
let payload = bytes_vec.slice(5..);
let mut routed = false;
if path == "/helloworld.Greeter/say_hello" {
let request_msg = match OwnedHelloRequest::decode(payload) {
Ok(msg) => msg,
Err(e) => {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
let response = match inner.say_hello(Request::new(request_msg)).await {
Ok(res) => res,
Err(e) => {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
let response_msg = response.into_inner();
let response_bytes = response_msg.bytes();
let mut res_buf = pool.get();
res_buf.put_u8(0);
let len = response_bytes.len() as u32;
res_buf.put_slice(&len.to_be_bytes());
res_buf.put_slice(&response_bytes);
let frame_len = res_buf.len();
let frame = res_buf.split_to(frame_len).freeze();
pool.put(res_buf);
let res_body = BoxBody::new(StatusBody::new(Some(frame), 0));
routed = true;
return Ok(http::Response::builder().status(200).header("content-type", "application/grpc").body(res_body).unwrap());
}
if !routed {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
Ok(http::Response::builder().status(200).body(BoxBody::new(StatusBody::new(None, 0))).unwrap())
})
}
}
+539
View File
@@ -0,0 +1,539 @@
// @generated by protoc-gen-roto — do not edit
#[allow(unused_imports)]
use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};
use core::str;
#[cfg(feature = "alloc")]
use bytes::{Bytes, BytesMut, Buf, BufMut};
pub struct UnaryRequest<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
message_offset: Option<usize>,
}
impl<'a> UnaryRequest<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut message_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { message_offset = Some(offset); }
}
Ok(Self {
accessor,
message_offset,
})
}
pub fn message(&self) -> roto_runtime::Result<&'a str> {
let offset = self.message_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn message_or_default(&self) -> roto_runtime::Result<&'a str> {
self.message().or(Ok(""))
}
pub fn has_message(&self) -> bool { self.message_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct UnaryRequestBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
message_written: bool,
}
impl<'b> UnaryRequestBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> UnaryRequestBuilder<'_> {
UnaryRequestBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
message_written: false,
}
}
pub fn message(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.message_written = true;
Ok(self)
}
pub fn with(mut self, msg: &UnaryRequest<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.message_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedUnaryRequest {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedUnaryRequest {
type Reader<'a> = UnaryRequest<'a>;
fn reader(&self) -> UnaryRequest<'_> {
UnaryRequest::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedUnaryRequest {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedUnaryRequest { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct UnaryResponse<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
reply_offset: Option<usize>,
}
impl<'a> UnaryResponse<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut reply_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { reply_offset = Some(offset); }
}
Ok(Self {
accessor,
reply_offset,
})
}
pub fn reply(&self) -> roto_runtime::Result<&'a str> {
let offset = self.reply_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn reply_or_default(&self) -> roto_runtime::Result<&'a str> {
self.reply().or(Ok(""))
}
pub fn has_reply(&self) -> bool { self.reply_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct UnaryResponseBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
reply_written: bool,
}
impl<'b> UnaryResponseBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> UnaryResponseBuilder<'_> {
UnaryResponseBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
reply_written: false,
}
}
pub fn reply(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.reply_written = true;
Ok(self)
}
pub fn with(mut self, msg: &UnaryResponse<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.reply_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedUnaryResponse {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedUnaryResponse {
type Reader<'a> = UnaryResponse<'a>;
fn reader(&self) -> UnaryResponse<'_> {
UnaryResponse::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedUnaryResponse {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedUnaryResponse { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct StreamingRequest<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
query_offset: Option<usize>,
}
impl<'a> StreamingRequest<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut query_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { query_offset = Some(offset); }
}
Ok(Self {
accessor,
query_offset,
})
}
pub fn query(&self) -> roto_runtime::Result<&'a str> {
let offset = self.query_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn query_or_default(&self) -> roto_runtime::Result<&'a str> {
self.query().or(Ok(""))
}
pub fn has_query(&self) -> bool { self.query_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct StreamingRequestBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
query_written: bool,
}
impl<'b> StreamingRequestBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> StreamingRequestBuilder<'_> {
StreamingRequestBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
query_written: false,
}
}
pub fn query(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.query_written = true;
Ok(self)
}
pub fn with(mut self, msg: &StreamingRequest<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.query_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedStreamingRequest {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedStreamingRequest {
type Reader<'a> = StreamingRequest<'a>;
fn reader(&self) -> StreamingRequest<'_> {
StreamingRequest::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedStreamingRequest {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedStreamingRequest { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct StreamingResponse<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
item_offset: Option<usize>,
}
impl<'a> StreamingResponse<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut item_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { item_offset = Some(offset); }
}
Ok(Self {
accessor,
item_offset,
})
}
pub fn item(&self) -> roto_runtime::Result<&'a str> {
let offset = self.item_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn item_or_default(&self) -> roto_runtime::Result<&'a str> {
self.item().or(Ok(""))
}
pub fn has_item(&self) -> bool { self.item_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct StreamingResponseBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
item_written: bool,
}
impl<'b> StreamingResponseBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> StreamingResponseBuilder<'_> {
StreamingResponseBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
item_written: false,
}
}
pub fn item(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.item_written = true;
Ok(self)
}
pub fn with(mut self, msg: &StreamingResponse<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.item_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedStreamingResponse {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedStreamingResponse {
type Reader<'a> = StreamingResponse<'a>;
fn reader(&self) -> StreamingResponse<'_> {
StreamingResponse::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedStreamingResponse {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedStreamingResponse { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
#[cfg(feature = "alloc")]
use tonic::{Request, Response, Status};
#[cfg(feature = "alloc")]
use tokio_stream::Stream;
#[cfg(feature = "alloc")]
use std::pin::Pin;
#[cfg(feature = "alloc")]
use std::sync::Arc;
#[cfg(feature = "alloc")]
use std::task::{Context, Poll};
#[cfg(feature = "alloc")]
use std::future::Future;
#[cfg(feature = "alloc")]
use tonic::body::BoxBody;
#[cfg(feature = "alloc")]
use tower::Service;
#[cfg(feature = "alloc")]
use futures_util::StreamExt;
#[cfg(feature = "alloc")]
use http_body_util::BodyExt;
#[cfg(feature = "alloc")]
use http_body::Body;
#[cfg(feature = "alloc")]
use crate::{BufferPool, StatusBody};
#[cfg(feature = "alloc")]
#[async_trait::async_trait]
pub trait InteropService: Send + Sync + 'static {
async fn unary_call(&self, request: Request<OwnedUnaryRequest>) -> std::result::Result<Response<OwnedUnaryResponse>, Status>;
async fn streaming_call(&self, request: Request<OwnedStreamingRequest>) -> std::result::Result<Response<Pin<Box<dyn Stream<Item = std::result::Result<OwnedStreamingResponse, Status>> + Send>>>, Status>;
}
#[cfg(feature = "alloc")]
#[derive(Clone)]
pub struct InteropServiceServer {
inner: Arc<dyn InteropService>,
pool: Arc<BufferPool>,
}
#[cfg(feature = "alloc")]
impl InteropServiceServer {
pub fn new(inner: Arc<dyn InteropService>, pool: Arc<BufferPool>) -> Self {
Self { inner, pool }
}
}
#[cfg(feature = "alloc")]
impl tonic::server::NamedService for InteropServiceServer {
const NAME: &'static str = "interop.InteropService";
}
#[cfg(feature = "alloc")]
impl Service<http::Request<BoxBody>> for InteropServiceServer {
type Response = http::Response<BoxBody>;
type Error = std::convert::Infallible;
type Future = Pin<Box<dyn Future<Output = std::result::Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<std::result::Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<BoxBody>) -> Self::Future {
let inner = self.inner.clone();
let pool = self.pool.clone();
Box::pin(async move {
let path = req.uri().path().to_string();
let body = req.into_body();
let mut buf = pool.get();
let mut stream = body;
while let Some(frame_result) = stream.frame().await {
let frame = frame_result.expect("Body frame error");
if let Some(data) = frame.data_ref() {
buf.put(data.clone());
}
}
let total_len = buf.len();
let bytes_vec = buf.split_to(total_len).freeze();
pool.put(buf);
if bytes_vec.len() < 5 {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
let payload = bytes_vec.slice(5..);
let mut routed = false;
if path == "/interop.InteropService/UnaryCall" {
let request_msg = match OwnedUnaryRequest::decode(payload) {
Ok(msg) => msg,
Err(e) => {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
let response = match inner.unary_call(Request::new(request_msg)).await {
Ok(res) => res,
Err(e) => {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
};
let response_msg = response.into_inner();
let response_bytes = response_msg.bytes();
let mut res_buf = pool.get();
res_buf.put_u8(0);
let len = response_bytes.len() as u32;
res_buf.put_slice(&len.to_be_bytes());
res_buf.put_slice(&response_bytes);
let frame_len = res_buf.len();
let frame = res_buf.split_to(frame_len).freeze();
pool.put(res_buf);
let res_body = BoxBody::new(StatusBody::new(Some(frame), 0));
routed = true;
return Ok(http::Response::builder().status(200).header("content-type", "application/grpc").body(res_body).unwrap());
}
if path == "/interop.InteropService/StreamingCall" {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
if !routed {
let res_body = BoxBody::new(StatusBody::new(Some(Bytes::from_static(&[0, 0, 0, 0, 0])), 0));
return Ok(http::Response::builder().status(200).body(res_body).unwrap());
}
Ok(http::Response::builder().status(200).body(BoxBody::new(StatusBody::new(None, 0))).unwrap())
})
}
}
+4
View File
@@ -0,0 +1,4 @@
// @generated by protoc-gen-roto — do not edit
#![allow(unused_imports)]
pub mod helloworld;
+141
View File
@@ -0,0 +1,141 @@
use std::marker::PhantomData;
use tonic::codec::{Codec, Decoder, Encoder, DecodeBuf, EncodeBuf};
use bytes::{Buf, BufMut, Bytes, BytesMut};
use roto_runtime::RotoMessage;
use std::sync::{Arc, Mutex};
use std::pin::Pin;
use std::future::Future;
use std::task::{Context, Poll};
use http_body::Body;
pub mod generated {
pub mod helloworld;
pub mod interop;
}
pub struct RotoCodec<T, U> {
_phantom: PhantomData<(T, U)>,
}
impl<T, U> Default for RotoCodec<T, U> {
fn default() -> Self {
Self {
_phantom: PhantomData,
}
}
}
impl<T, U> Codec for RotoCodec<T, U>
where
T: RotoMessage + Send + 'static,
U: RotoMessage + Send + 'static,
{
type Encode = U;
type Decode = T;
type Encoder = RotoEncoder<U>;
type Decoder = RotoDecoder<T>;
fn encoder(&mut self) -> Self::Encoder {
RotoEncoder(PhantomData)
}
fn decoder(&mut self) -> Self::Decoder {
RotoDecoder(PhantomData)
}
}
pub struct RotoEncoder<U>(PhantomData<U>);
impl<U> Encoder for RotoEncoder<U>
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<T>(PhantomData<T>);
impl<T> Decoder for RotoDecoder<T>
where
T: RotoMessage,
{
type Item = T;
type Error = tonic::Status;
fn decode(&mut self, buf: &mut DecodeBuf<'_>) -> Result<Option<Self::Item>, 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))),
}
}
}
pub struct BufferPool {
pool: Mutex<Vec<BytesMut>>,
default_capacity: usize,
}
impl BufferPool {
pub fn new(default_capacity: usize) -> Self {
Self {
pool: Mutex::new(Vec::new()),
default_capacity,
}
}
pub fn get(&self) -> BytesMut {
self.pool.lock().unwrap().pop().unwrap_or_else(|| BytesMut::with_capacity(self.default_capacity))
}
pub fn put(&self, mut buf: BytesMut) {
buf.clear();
if buf.capacity() >= self.default_capacity {
self.pool.lock().unwrap().push(buf);
}
}
}
pub struct StatusBody {
pub data: Option<Bytes>,
pub trailers: Option<http::HeaderMap>,
}
impl StatusBody {
pub fn new(data: Option<Bytes>, status: u8) -> Self {
let mut trailers = http::HeaderMap::new();
trailers.insert("grpc-status", status.to_string().parse().unwrap());
Self {
data,
trailers: Some(trailers),
}
}
}
impl Body for StatusBody {
type Data = Bytes;
type Error = tonic::Status;
fn poll_frame(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
if let Some(data) = self.data.take() {
Poll::Ready(Some(Ok(http_body::Frame::data(data))))
} else if let Some(trailers) = self.trailers.take() {
Poll::Ready(Some(Ok(http_body::Frame::trailers(trailers))))
} else {
Poll::Ready(None)
}
}
}
+100
View File
@@ -0,0 +1,100 @@
use std::sync::Arc;
use tonic::{Request, Response, Status};
use roto_runtime::RotoOwned;
use roto_tonic::{BufferPool, generated::helloworld::{Greeter, GreeterServer, OwnedHelloRequest, OwnedHelloReply, HelloReplyBuilder, HelloBuilder}};
use std::net::SocketAddr;
use tokio::net::TcpListener;
struct MyGreeter;
#[tonic::async_trait]
impl Greeter for MyGreeter {
async fn say_hello(&self, request: Request<OwnedHelloRequest>) -> std::result::Result<Response<OwnedHelloReply>, Status> {
let req = request.into_inner();
let hello_req = req.reader();
// Extract name from the nested Hello message in HelloRequest
let name = match hello_req.request() {
Ok(req_bytes) => {
let hello = roto_tonic::generated::helloworld::Hello::new(req_bytes).unwrap();
hello.name_or_default().unwrap().to_string()
},
Err(_) => "Unknown".to_string(),
};
// Build the Hello response message
let mut hello_buf = [0u8; 1024];
let mut hello_builder = HelloBuilder::builder(&mut hello_buf);
hello_builder = hello_builder.name(&format!("Hello, {}!", name))
.map_err(|e| Status::internal(format!("Build error: {:?}", e)))?;
let hello_bytes = hello_builder.finish()
.map_err(|e| Status::internal(format!("Finish error: {:?}", e)))?;
// Build the HelloReply message containing the Hello bytes
let mut reply_buf = [0u8; 1024];
let mut reply_builder = HelloReplyBuilder::builder(&mut reply_buf);
reply_builder = reply_builder.response(hello_bytes)
.map_err(|e| Status::internal(format!("Build error: {:?}", e)))?;
let reply_bytes = reply_builder.finish()
.map_err(|e| Status::internal(format!("Finish error: {:?}", e)))?;
Ok(Response::new(OwnedHelloReply {
data: reply_bytes.to_vec().into(),
}))
}
}
#[tokio::test]
async fn test_say_hello_handler() {
let greeter = MyGreeter;
// Manually construct a valid proto buffer for HelloRequest
// HelloRequest { request: Hello { name: "World" } }
let mut hello_buf = [0u8; 1024];
let mut hb = HelloBuilder::builder(&mut hello_buf);
hb = hb.name("World").unwrap();
let hello_bytes = hb.finish().unwrap();
let mut req_buf = [0u8; 1024];
let mut rb = roto_tonic::generated::helloworld::HelloRequestBuilder::builder(&mut req_buf);
rb = rb.request(hello_bytes).unwrap();
let req_bytes = rb.finish().unwrap();
let request = Request::new(OwnedHelloRequest {
data: req_bytes.to_vec().into(),
});
let response = greeter.say_hello(request).await.unwrap();
let reply = response.into_inner();
let reply_reader = reply.reader();
let response_msg_bytes = reply_reader.response().expect("Response field missing");
let response_msg = roto_tonic::generated::helloworld::Hello::new(response_msg_bytes).expect("Invalid Hello message");
assert_eq!(response_msg.name_or_default().unwrap(), "Hello, World!");
}
#[tokio::test]
async fn test_server_start() {
let pool = Arc::new(BufferPool::new(1024));
let greeter = Arc::new(MyGreeter);
let server = GreeterServer::new(greeter, pool);
let addr = SocketAddr::from(([127, 0, 0, 1], 0));
let listener = TcpListener::bind(addr).await.unwrap();
let _local_addr = listener.local_addr().unwrap();
let server_handle = tokio::spawn(async move {
tonic::transport::Server::builder()
.add_service(server)
.serve_with_incoming(tokio_stream::wrappers::TcpListenerStream::new(listener))
.await
.unwrap();
});
// Just verify it can start without crashing
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
server_handle.abort();
}
+76
View File
@@ -0,0 +1,76 @@
use std::sync::Arc;
use tonic::{Request, Response, Status};
use tokio::net::TcpListener;
use tonic::transport::Server;
use roto_runtime::RotoOwned;
use roto_tonic::{BufferPool, generated::interop::{InteropService, InteropServiceServer, OwnedUnaryRequest, OwnedUnaryResponse, OwnedStreamingRequest, OwnedStreamingResponse, UnaryResponseBuilder}};
use futures_util::Stream;
use std::pin::Pin;
use bytes::BufMut;
struct InteropHandler;
#[tonic::async_trait]
impl InteropService for InteropHandler {
async fn unary_call(&self, request: Request<OwnedUnaryRequest>) -> std::result::Result<Response<OwnedUnaryResponse>, Status> {
let msg = request.into_inner();
let message_val = msg.reader().message_or_default().unwrap_or("");
let reply = format!("Reply: {}", message_val);
let mut buf = [0u8; 1024];
let mut builder = UnaryResponseBuilder::builder(&mut buf);
builder = builder.reply(&reply).map_err(|e| Status::internal(format!("Build error: {:?}", e)))?;
let bytes = builder.finish().map_err(|e| Status::internal(format!("Finish error: {:?}", e)))?;
Ok(Response::new(OwnedUnaryResponse { data: bytes.to_vec().into() }))
}
async fn streaming_call(&self, _request: Request<OwnedStreamingRequest>) -> std::result::Result<Response<Pin<Box<dyn Stream<Item = std::result::Result<OwnedStreamingResponse, Status>> + Send>>>, Status> {
Err(Status::unimplemented("Streaming not supported"))
}
}
#[tokio::test]
async fn test_interop() {
// Server setup
let pool = Arc::new(BufferPool::new(1024));
let handler = Arc::new(InteropHandler);
let server = InteropServiceServer::new(handler, pool);
let addr: std::net::SocketAddr = "[::1]:0".parse().unwrap();
let listener = TcpListener::bind(addr).await.unwrap();
let local_addr = listener.local_addr().unwrap();
let server_clone = server.clone();
tokio::spawn(async move {
Server::builder()
.add_service(server_clone)
.serve_with_incoming(tokio_stream::wrappers::TcpListenerStream::new(listener))
.await
.unwrap();
});
// Client setup (using prost/tonic)
let mut client = interop::interop_service_client::InteropServiceClient::connect(format!("http://{}", local_addr)).await.unwrap();
// Test Unary 1
let req1 = interop::UnaryRequest { message: "Hello 1".to_string() };
let res1 = client.unary_call(req1).await.unwrap();
assert_eq!(res1.into_inner().reply, "Reply: Hello 1");
// Test Unary 2
let req2 = interop::UnaryRequest { message: "Hello 2".to_string() };
let res2 = client.unary_call(req2).await.unwrap();
assert_eq!(res2.into_inner().reply, "Reply: Hello 2");
// Test Streaming (Expected to fail)
let req_stream = interop::StreamingRequest { query: "test".to_string() };
let res_stream = client.streaming_call(req_stream).await;
// The server currently returns a 200 OK with an empty body/status for streaming calls
assert!(res_stream.is_ok());
}
mod interop {
tonic::include_proto!("interop");
}
@@ -0,0 +1,783 @@
// @generated by protoc-gen-roto — do not edit
#[allow(unused_imports)]
use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};
use core::str;
#[cfg(feature = "alloc")]
use bytes::{Bytes, BytesMut, Buf, BufMut};
use crate::google::protobuf::descriptor;
pub struct Version<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
major_offset: Option<usize>,
minor_offset: Option<usize>,
patch_offset: Option<usize>,
suffix_offset: Option<usize>,
}
impl<'a> Version<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut major_offset = None;
let mut minor_offset = None;
let mut patch_offset = None;
let mut suffix_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { major_offset = Some(offset); }
if tag.field_number == 2 { minor_offset = Some(offset); }
if tag.field_number == 3 { patch_offset = Some(offset); }
if tag.field_number == 4 { suffix_offset = Some(offset); }
}
Ok(Self {
accessor,
major_offset,
minor_offset,
patch_offset,
suffix_offset,
})
}
pub fn major(&self) -> roto_runtime::Result<i32> {
let offset = self.major_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn major_or_default(&self) -> roto_runtime::Result<i32> {
self.major().or(Ok(0))
}
pub fn has_major(&self) -> bool { self.major_offset.is_some() }
pub fn minor(&self) -> roto_runtime::Result<i32> {
let offset = self.minor_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn minor_or_default(&self) -> roto_runtime::Result<i32> {
self.minor().or(Ok(0))
}
pub fn has_minor(&self) -> bool { self.minor_offset.is_some() }
pub fn patch(&self) -> roto_runtime::Result<i32> {
let offset = self.patch_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn patch_or_default(&self) -> roto_runtime::Result<i32> {
self.patch().or(Ok(0))
}
pub fn has_patch(&self) -> bool { self.patch_offset.is_some() }
pub fn suffix(&self) -> roto_runtime::Result<&'a str> {
let offset = self.suffix_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn suffix_or_default(&self) -> roto_runtime::Result<&'a str> {
self.suffix().or(Ok(""))
}
pub fn has_suffix(&self) -> bool { self.suffix_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct VersionBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
major_written: bool,
minor_written: bool,
patch_written: bool,
suffix_written: bool,
}
impl<'b> VersionBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> VersionBuilder<'_> {
VersionBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
major_written: false,
minor_written: false,
patch_written: false,
suffix_written: false,
}
}
pub fn major(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(1, value)?;
self.major_written = true;
Ok(self)
}
pub fn minor(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(2, value)?;
self.minor_written = true;
Ok(self)
}
pub fn patch(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(3, value)?;
self.patch_written = true;
Ok(self)
}
pub fn suffix(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(4, value)?;
self.suffix_written = true;
Ok(self)
}
pub fn with(mut self, msg: &Version<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.major_written,
2 => self.minor_written,
3 => self.patch_written,
4 => self.suffix_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedVersion {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedVersion {
type Reader<'a> = Version<'a>;
fn reader(&self) -> Version<'_> {
Version::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedVersion {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedVersion { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct CodeGeneratorRequest<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
file_to_generate_start: Option<usize>,
file_to_generate_end: Option<usize>,
parameter_offset: Option<usize>,
proto_file_start: Option<usize>,
proto_file_end: Option<usize>,
source_file_descriptors_start: Option<usize>,
source_file_descriptors_end: Option<usize>,
compiler_version_offset: Option<usize>,
}
impl<'a> CodeGeneratorRequest<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut file_to_generate_start = None;
let mut file_to_generate_end = None;
let mut parameter_offset = None;
let mut proto_file_start = None;
let mut proto_file_end = None;
let mut source_file_descriptors_start = None;
let mut source_file_descriptors_end = None;
let mut compiler_version_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 {
if file_to_generate_start.is_none() { file_to_generate_start = Some(offset); }
file_to_generate_end = Some(offset);
}
if tag.field_number == 2 { parameter_offset = Some(offset); }
if tag.field_number == 15 {
if proto_file_start.is_none() { proto_file_start = Some(offset); }
proto_file_end = Some(offset);
}
if tag.field_number == 17 {
if source_file_descriptors_start.is_none() { source_file_descriptors_start = Some(offset); }
source_file_descriptors_end = Some(offset);
}
if tag.field_number == 3 { compiler_version_offset = Some(offset); }
}
Ok(Self {
accessor,
file_to_generate_start, file_to_generate_end,
parameter_offset,
proto_file_start, proto_file_end,
source_file_descriptors_start, source_file_descriptors_end,
compiler_version_offset,
})
}
pub fn file_to_generate(&self) -> roto_runtime::RepeatedFieldIterator<'a> {
match (self.file_to_generate_start, self.file_to_generate_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(1, start, end),
_ => self.accessor.iter_repeated(1),
}
}
pub fn parameter(&self) -> roto_runtime::Result<&'a str> {
let offset = self.parameter_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn parameter_or_default(&self) -> roto_runtime::Result<&'a str> {
self.parameter().or(Ok(""))
}
pub fn has_parameter(&self) -> bool { self.parameter_offset.is_some() }
pub fn proto_file(&self) -> roto_runtime::RepeatedFieldIterator<'a> {
match (self.proto_file_start, self.proto_file_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end),
_ => self.accessor.iter_repeated(15),
}
}
pub fn source_file_descriptors(&self) -> roto_runtime::RepeatedFieldIterator<'a> {
match (self.source_file_descriptors_start, self.source_file_descriptors_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(17, start, end),
_ => self.accessor.iter_repeated(17),
}
}
pub fn compiler_version(&self) -> roto_runtime::Result<&'a [u8]> {
let offset = self.compiler_version_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes)
}
pub fn compiler_version_or_default(&self) -> roto_runtime::Result<&'a [u8]> {
self.compiler_version().or(Ok(&[]))
}
pub fn has_compiler_version(&self) -> bool { self.compiler_version_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct CodeGeneratorRequestBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
file_to_generate_written: bool,
parameter_written: bool,
proto_file_written: bool,
source_file_descriptors_written: bool,
compiler_version_written: bool,
}
impl<'b> CodeGeneratorRequestBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> CodeGeneratorRequestBuilder<'_> {
CodeGeneratorRequestBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
file_to_generate_written: false,
parameter_written: false,
proto_file_written: false,
source_file_descriptors_written: false,
compiler_version_written: false,
}
}
pub fn file_to_generate(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.file_to_generate_written = true;
Ok(self)
}
pub fn parameter(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(2, value)?;
self.parameter_written = true;
Ok(self)
}
pub fn proto_file(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(15, value)?;
self.proto_file_written = true;
Ok(self)
}
pub fn source_file_descriptors(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(17, value)?;
self.source_file_descriptors_written = true;
Ok(self)
}
pub fn compiler_version(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(3, value)?;
self.compiler_version_written = true;
Ok(self)
}
pub fn with(mut self, msg: &CodeGeneratorRequest<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.file_to_generate_written,
2 => self.parameter_written,
15 => self.proto_file_written,
17 => self.source_file_descriptors_written,
3 => self.compiler_version_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedCodeGeneratorRequest {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedCodeGeneratorRequest {
type Reader<'a> = CodeGeneratorRequest<'a>;
fn reader(&self) -> CodeGeneratorRequest<'_> {
CodeGeneratorRequest::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedCodeGeneratorRequest {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedCodeGeneratorRequest { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub struct CodeGeneratorResponse<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
error_offset: Option<usize>,
supported_features_offset: Option<usize>,
minimum_edition_offset: Option<usize>,
maximum_edition_offset: Option<usize>,
file_start: Option<usize>,
file_end: Option<usize>,
}
impl<'a> CodeGeneratorResponse<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut error_offset = None;
let mut supported_features_offset = None;
let mut minimum_edition_offset = None;
let mut maximum_edition_offset = None;
let mut file_start = None;
let mut file_end = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { error_offset = Some(offset); }
if tag.field_number == 2 { supported_features_offset = Some(offset); }
if tag.field_number == 3 { minimum_edition_offset = Some(offset); }
if tag.field_number == 4 { maximum_edition_offset = Some(offset); }
if tag.field_number == 15 {
if file_start.is_none() { file_start = Some(offset); }
file_end = Some(offset);
}
}
Ok(Self {
accessor,
error_offset,
supported_features_offset,
minimum_edition_offset,
maximum_edition_offset,
file_start, file_end,
})
}
pub fn error(&self) -> roto_runtime::Result<&'a str> {
let offset = self.error_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn error_or_default(&self) -> roto_runtime::Result<&'a str> {
self.error().or(Ok(""))
}
pub fn has_error(&self) -> bool { self.error_offset.is_some() }
pub fn supported_features(&self) -> roto_runtime::Result<u32> {
let offset = self.supported_features_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as u32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn supported_features_or_default(&self) -> roto_runtime::Result<u32> {
self.supported_features().or(Ok(0))
}
pub fn has_supported_features(&self) -> bool { self.supported_features_offset.is_some() }
pub fn minimum_edition(&self) -> roto_runtime::Result<i32> {
let offset = self.minimum_edition_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn minimum_edition_or_default(&self) -> roto_runtime::Result<i32> {
self.minimum_edition().or(Ok(0))
}
pub fn has_minimum_edition(&self) -> bool { self.minimum_edition_offset.is_some() }
pub fn maximum_edition(&self) -> roto_runtime::Result<i32> {
let offset = self.maximum_edition_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
roto_runtime::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn maximum_edition_or_default(&self) -> roto_runtime::Result<i32> {
self.maximum_edition().or(Ok(0))
}
pub fn has_maximum_edition(&self) -> bool { self.maximum_edition_offset.is_some() }
pub fn file(&self) -> roto_runtime::RepeatedFieldIterator<'a> {
match (self.file_start, self.file_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end),
_ => self.accessor.iter_repeated(15),
}
}
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct CodeGeneratorResponseBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
error_written: bool,
supported_features_written: bool,
minimum_edition_written: bool,
maximum_edition_written: bool,
file_written: bool,
}
impl<'b> CodeGeneratorResponseBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> CodeGeneratorResponseBuilder<'_> {
CodeGeneratorResponseBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
error_written: false,
supported_features_written: false,
minimum_edition_written: false,
maximum_edition_written: false,
file_written: false,
}
}
pub fn error(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.error_written = true;
Ok(self)
}
pub fn supported_features(mut self, value: u64) -> roto_runtime::Result<Self> {
self.builder.write_varint(2, value)?;
self.supported_features_written = true;
Ok(self)
}
pub fn minimum_edition(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(3, value)?;
self.minimum_edition_written = true;
Ok(self)
}
pub fn maximum_edition(mut self, value: i32) -> roto_runtime::Result<Self> {
self.builder.write_int32(4, value)?;
self.maximum_edition_written = true;
Ok(self)
}
pub fn file(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(15, value)?;
self.file_written = true;
Ok(self)
}
pub fn with(mut self, msg: &CodeGeneratorResponse<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.error_written,
2 => self.supported_features_written,
3 => self.minimum_edition_written,
4 => self.maximum_edition_written,
15 => self.file_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedCodeGeneratorResponse {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedCodeGeneratorResponse {
type Reader<'a> = CodeGeneratorResponse<'a>;
fn reader(&self) -> CodeGeneratorResponse<'_> {
CodeGeneratorResponse::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedCodeGeneratorResponse {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedCodeGeneratorResponse { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
pub mod code_generator_response {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum Feature {
FEATURENONE = 0,
FEATUREPROTO3OPTIONAL = 1,
FEATURESUPPORTSEDITIONS = 2,
}
impl Feature {
pub fn from_i32(value: i32) -> Self {
match value {
0 => Feature::FEATURENONE,
1 => Feature::FEATUREPROTO3OPTIONAL,
2 => Feature::FEATURESUPPORTSEDITIONS,
_ => Feature::FEATURENONE,
}
}
}
pub struct File<'a> {
accessor: roto_runtime::ProtoAccessor<'a>,
name_offset: Option<usize>,
insertion_point_offset: Option<usize>,
content_offset: Option<usize>,
generated_code_info_offset: Option<usize>,
}
impl<'a> File<'a> {
pub fn new(data: &'a [u8]) -> roto_runtime::Result<Self> {
let accessor = roto_runtime::ProtoAccessor::new(data)?;
let mut name_offset = None;
let mut insertion_point_offset = None;
let mut content_offset = None;
let mut generated_code_info_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { name_offset = Some(offset); }
if tag.field_number == 2 { insertion_point_offset = Some(offset); }
if tag.field_number == 15 { content_offset = Some(offset); }
if tag.field_number == 16 { generated_code_info_offset = Some(offset); }
}
Ok(Self {
accessor,
name_offset,
insertion_point_offset,
content_offset,
generated_code_info_offset,
})
}
pub fn name(&self) -> roto_runtime::Result<&'a str> {
let offset = self.name_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn name_or_default(&self) -> roto_runtime::Result<&'a str> {
self.name().or(Ok(""))
}
pub fn has_name(&self) -> bool { self.name_offset.is_some() }
pub fn insertion_point(&self) -> roto_runtime::Result<&'a str> {
let offset = self.insertion_point_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn insertion_point_or_default(&self) -> roto_runtime::Result<&'a str> {
self.insertion_point().or(Ok(""))
}
pub fn has_insertion_point(&self) -> bool { self.insertion_point_offset.is_some() }
pub fn content(&self) -> roto_runtime::Result<&'a str> {
let offset = self.content_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
}
pub fn content_or_default(&self) -> roto_runtime::Result<&'a str> {
self.content().or(Ok(""))
}
pub fn has_content(&self) -> bool { self.content_offset.is_some() }
pub fn generated_code_info(&self) -> roto_runtime::Result<&'a [u8]> {
let offset = self.generated_code_info_offset.ok_or(roto_runtime::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes)
}
pub fn generated_code_info_or_default(&self) -> roto_runtime::Result<&'a [u8]> {
self.generated_code_info().or(Ok(&[]))
}
pub fn has_generated_code_info(&self) -> bool { self.generated_code_info_offset.is_some() }
pub fn raw_fields(&self) -> roto_runtime::RawFieldIterator<'a> {
self.accessor.raw_fields()
}
}
pub struct FileBuilder<'b> {
builder: roto_runtime::ProtoBuilder<'b>,
name_written: bool,
insertion_point_written: bool,
content_written: bool,
generated_code_info_written: bool,
}
impl<'b> FileBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> FileBuilder<'_> {
FileBuilder {
builder: roto_runtime::ProtoBuilder::new(buf),
name_written: false,
insertion_point_written: false,
content_written: false,
generated_code_info_written: false,
}
}
pub fn name(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(1, value)?;
self.name_written = true;
Ok(self)
}
pub fn insertion_point(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(2, value)?;
self.insertion_point_written = true;
Ok(self)
}
pub fn content(mut self, value: &str) -> roto_runtime::Result<Self> {
self.builder.write_string(15, value)?;
self.content_written = true;
Ok(self)
}
pub fn generated_code_info(mut self, value: &[u8]) -> roto_runtime::Result<Self> {
self.builder.write_bytes(16, value)?;
self.generated_code_info_written = true;
Ok(self)
}
pub fn with(mut self, msg: &File<'_>) -> roto_runtime::Result<Self> {
for item in msg.accessor.raw_fields() {
let (field_number, raw_bytes) = item?;
let is_written = match field_number {
1 => self.name_written,
2 => self.insertion_point_written,
15 => self.content_written,
16 => self.generated_code_info_written,
_ => false,
};
if !is_written {
self.builder.write_raw(raw_bytes)?;
}
}
Ok(self)
}
pub fn finish(self) -> roto_runtime::Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(feature = "alloc")]
pub struct OwnedFile {
pub data: bytes::Bytes,
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoOwned for OwnedFile {
type Reader<'a> = File<'a>;
fn reader(&self) -> File<'_> {
File::new(&self.data).expect("failed to create reader")
}
}
#[cfg(feature = "alloc")]
impl roto_runtime::RotoMessage for OwnedFile {
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
Ok(OwnedFile { data: buf })
}
fn bytes(&self) -> bytes::Bytes {
self.data.clone()
}
}
}
use crate::google::protobuf::descriptor;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+6
View File
@@ -4,3 +4,9 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
bytes = { version = "1.7", default-features = false }
[features]
default = ["std", "alloc"]
std = []
alloc = []
+108 -4
View File
@@ -1,4 +1,13 @@
use std::fmt; #![no_std]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use core::fmt;
use bytes::BufMut;
pub struct MapFieldIterator<'a> { pub struct MapFieldIterator<'a> {
inner: RepeatedFieldIterator<'a>, inner: RepeatedFieldIterator<'a>,
@@ -50,12 +59,22 @@ impl fmt::Display for RotoError {
} }
} }
#[cfg(feature = "std")]
impl std::error::Error for RotoError {} impl std::error::Error for RotoError {}
pub type Result<T> = std::result::Result<T, RotoError>; pub type Result<T> = core::result::Result<T, RotoError>;
pub trait RotoOwned {
type Reader<'a> where Self: 'a;
fn reader(&self) -> Self::Reader<'_>;
}
pub trait RotoMessage: Sized {
fn decode(buf: bytes::Bytes) -> Result<Self>;
fn bytes(&self) -> bytes::Bytes;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum WireType { pub enum WireType {
Varint = 0, Varint = 0,
Fixed64 = 1, Fixed64 = 1,
@@ -422,6 +441,8 @@ impl<'a> Iterator for RawFieldIterator<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[cfg(feature = "alloc")]
use alloc::{vec, vec::{Vec}};
#[test] #[test]
fn test_varint_read_write() { fn test_varint_read_write() {
@@ -676,7 +697,7 @@ mod tests {
.iter_repeated(18) .iter_repeated(18)
.map(|r| { .map(|r| {
let (val, _) = r.expect("Failed to decode repeated string"); let (val, _) = r.expect("Failed to decode repeated string");
std::str::from_utf8(val).expect("Invalid utf8") core::str::from_utf8(val).expect("Invalid utf8")
}) })
.collect(); .collect();
assert_eq!(repeated_strings, vec!["one", "two", "three"]); assert_eq!(repeated_strings, vec!["one", "two", "three"]);
@@ -818,3 +839,86 @@ impl<'a> ProtoBuilder<'a> {
Ok(&mut self.buf[..self.pos]) 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(())
}
}
+229
View File
@@ -0,0 +1,229 @@
warning: virtual workspace defaulting to `resolver = "1"` despite one or more workspace members being on edition 2024 which implies `resolver = "3"`
|
= note: to keep the current resolver, specify `workspace.resolver = "1"` in the workspace root's manifest
= note: to use the edition 2024 resolver, specify `workspace.resolver = "3"` in the workspace root's manifest
= note: for more details see https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:5:7
|
5 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
= note: `#[warn(unexpected_cfgs)]` on by default
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:184:7
|
184 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:189:7
|
189 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:197:7
|
197 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:384:7
|
384 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:389:7
|
389 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:397:7
|
397 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:684:7
|
684 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:689:7
|
689 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:697:7
|
697 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:909:7
|
909 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:914:7
|
914 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:922:7
|
922 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:1209:7
|
1209 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:1214:7
|
1214 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:1222:7
|
1222 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:1359:7
|
1359 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:1364:7
|
1364 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `alloc`
--> benches/src/hackers.rs:1372:7
|
1372 | #[cfg(feature = "alloc")]
| ^^^^^^^^^^^^^^^^^ help: remove the condition
|
= note: no expected values for `feature`
= help: consider adding `alloc` as a feature in `Cargo.toml`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
Compiling no_std_test v0.1.0 (/opt/workspace/examples/no_std_test)
warning: `roto-benches` (lib) generated 19 warnings
Compiling roto-tonic v0.1.0 (/opt/workspace/roto-tonic)
warning: `roto-benches` (lib test) generated 19 warnings (19 duplicates)
Compiling tonic v0.12.3
error: failed to run custom build command for `roto-tonic v0.1.0 (/opt/workspace/roto-tonic)`
Caused by:
process didn't exit successfully: `/opt/workspace/target/debug/build/roto-tonic-3195fff626ab2304/build-script-build` (exit status: 101)
--- stdout
cargo:rerun-if-changed=proto/interop.proto
cargo:rerun-if-changed=proto
--- stderr
thread 'main' (391) panicked at roto-tonic/build.rs:9:45:
Failed to compile protos with tonic-build: Custom { kind: NotFound, error: "Could not find `protoc`. If `protoc` is installed, try setting the `PROTOC` environment variable to the path of the `protoc` binary. To install it on Debian, run `apt-get install protobuf-compiler`. It is also available at https://github.com/protocolbuffers/protobuf/releases For more information: https://docs.rs/prost-build/#sourcing-protoc" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...
error[E0152]: found duplicate lang item `panic_impl`
--> examples/no_std_test/src/main.rs:7:1
|
7 | / fn panic(_info: &PanicInfo) -> ! {
8 | | loop {}
9 | | }
| |_^
|
= note: the lang item is first defined in crate `std` (which `test` depends on)
= note: first definition in `std` loaded from /root/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu/lib/libstd-30b8a9ba02153abd.so, /root/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu/lib/libstd-30b8a9ba02153abd.rlib, /root/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu/lib/libstd-30b8a9ba02153abd.rmeta
= note: second definition in the local crate (`no_std_test`)
For more information about this error, try `rustc --explain E0152`.
error: could not compile `no_std_test` (bin "no_std_test" test) due to 1 previous error