Compare commits

..

4 Commits

Author SHA1 Message Date
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
14 changed files with 1769 additions and 72 deletions
Generated
+2
View File
@@ -1201,6 +1201,8 @@ dependencies = [
"http-body-util",
"prost",
"roto-runtime",
"tokio",
"tokio-stream",
"tonic",
"tower 0.4.13",
]
+15 -4
View File
@@ -562,7 +562,7 @@ pub fn generate_rust_code(
output.push_str("use futures_util::StreamExt;\n");
output.push_str("use http_body_util::BodyExt;\n");
output.push_str("use http_body::Body;\n");
output.push_str("use roto_tonic::{BufferPool, StatusBody};\n\n");
output.push_str("use crate::{BufferPool, StatusBody};\n\n");
for dep_res in file_proto.dependency() {
let (dep_data, _) = dep_res.expect("Failed to iterate dependency");
@@ -595,6 +595,7 @@ pub fn generate_rust_code(
let (svc_data, _) = svc_res.expect("Failed to iterate service");
write_service(
&ServiceDescriptorProto::new(svc_data).expect("Failed to parse ServiceDescriptorProto"),
file_proto.package().unwrap_or(""),
&mut output,
);
}
@@ -654,7 +655,7 @@ pub fn generate_rust_code(
generated_files
}
fn write_service(svc_proto: &ServiceDescriptorProto, output: &mut String) {
fn write_service(svc_proto: &ServiceDescriptorProto, package: &str, output: &mut String) {
let svc_name = to_pascal_case(svc_proto.name().unwrap());
output.push_str(&format!("#[tonic::async_trait]\npub trait {}: Send + Sync + 'static {{\n", svc_name));
@@ -707,7 +708,12 @@ fn write_service(svc_proto: &ServiceDescriptorProto, output: &mut String) {
output.push_str("}\n\n");
output.push_str(&format!("impl tonic::server::NamedService for {} {{\n", server_name));
output.push_str(&format!(" const NAME: &'static str = \"{}\";\n", svc_proto.name().unwrap()));
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));
@@ -756,7 +762,12 @@ fn write_service(svc_proto: &ServiceDescriptorProto, output: &mut String) {
}
for (method_name, input_owned) in methods {
output.push_str(&format!(" if path == \"/{}/{}\" {{\n", svc_proto.name().unwrap(), method_name));
let full_path = if package.is_empty() {
format!("/{}/{}", svc_proto.name().unwrap(), method_name)
} else {
format!("/{}.{}/{}", package, svc_proto.name().unwrap(), 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");
+3 -2
View File
@@ -68,10 +68,11 @@ fn test_generated_code_builds() {
// 4. Write the generated code to src/lib.rs
// 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();
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");
}
let lib_path = temp_project_dir.join("src/lib.rs");
+3 -3
View File
@@ -55,12 +55,12 @@ fn test_helloworld_generated_code_builds() {
// 4. Write the generated code to src/lib.rs
let mut all_code = String::new();
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");
}
let final_code = all_code.replace("use crate::", "use roto::");
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
let build_status = Command::new("cargo")
+10 -6
View File
@@ -49,22 +49,26 @@ fn test_map_generated_code_builds() {
// 4. Write the generated code to src/lib.rs
let mut all_code = String::new();
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");
}
let final_code = all_code.replace("use crate::", "use roto::");
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
let build_status = Command::new("cargo")
let output = Command::new("cargo")
.args(["build"])
.current_dir(&temp_project_dir)
.status()
.output()
.expect("Failed to run cargo build");
if !output.status.success() {
eprintln!("Cargo build failed:\n{}", String::from_utf8_lossy(&output.stderr));
}
assert!(
build_status.success(),
output.status.success(),
"The generated Rust code for test_map.proto failed to build in a standalone project!"
);
}
+3 -8
View File
@@ -49,17 +49,12 @@ fn test_types_generated_code_builds() {
// 4. Write the generated code to src/lib.rs
let mut all_code = String::new();
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");
}
// 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");
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
let build_status = Command::new("cargo")
+7 -2
View File
@@ -5,6 +5,8 @@ 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"));
}
@@ -19,11 +21,14 @@ where
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
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)
}
}
+6 -47
View File
@@ -13,35 +13,12 @@ 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"));
}
struct BufferPool {
pool: Mutex<Vec<BytesMut>>,
default_capacity: usize,
}
impl BufferPool {
fn new(default_capacity: usize) -> Self {
Self {
pool: Mutex::new(Vec::new()),
default_capacity,
}
}
fn get(&self) -> BytesMut {
self.pool.lock().unwrap().pop().unwrap_or_else(|| BytesMut::with_capacity(self.default_capacity))
}
fn put(&self, mut buf: BytesMut) {
buf.clear();
if buf.capacity() >= self.default_capacity {
self.pool.lock().unwrap().push(buf);
}
}
}
#[derive(Clone)]
pub struct MyHelloWorld {
pool: Arc<BufferPool>,
@@ -99,24 +76,6 @@ impl tonic::server::NamedService for HelloWorldServer {
const NAME: &'static str = "hello.HelloWorldService";
}
struct StatusBody(Option<Bytes>);
impl Body for StatusBody {
type Data = Bytes;
type Error = 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.0.take() {
Poll::Ready(Some(Ok(http_body::Frame::data(data))))
} else {
Poll::Ready(None)
}
}
}
impl Service<http::Request<BoxBody>> for HelloWorldServer {
type Response = http::Response<BoxBody>;
type Error = std::convert::Infallible;
@@ -152,7 +111,7 @@ impl Service<http::Request<BoxBody>> for HelloWorldServer {
if bytes_vec.len() < 5 {
println!("Body too short: {} bytes", bytes_vec.len());
let res_body = BoxBody::new(StatusBody(Some(Bytes::from_static(&[0, 0, 0, 0, 0]))));
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)
@@ -164,7 +123,7 @@ impl Service<http::Request<BoxBody>> for HelloWorldServer {
Ok(msg) => msg,
Err(e) => {
println!("Decode error: {}", e);
let res_body = BoxBody::new(StatusBody(Some(Bytes::from_static(&[0, 0, 0, 0, 0]))));
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());
}
};
@@ -174,7 +133,7 @@ impl Service<http::Request<BoxBody>> for HelloWorldServer {
Ok(res) => res,
Err(e) => {
println!("Service error: {}", e);
let res_body = BoxBody::new(StatusBody(Some(Bytes::from_static(&[0, 0, 0, 0, 0]))));
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());
}
};
@@ -193,7 +152,7 @@ impl Service<http::Request<BoxBody>> for HelloWorldServer {
let frame = res_buf.split_to(frame_len).freeze();
pool.put(res_buf);
let res_body = BoxBody::new(StatusBody(Some(frame)));
let res_body = BoxBody::new(StatusBody::new(Some(frame), 0));
Ok(http::Response::builder()
.status(200)
.header("content-type", "application/grpc")
+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())
})
}
}
+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;
}
+2
View File
@@ -12,4 +12,6 @@ http-body = "1.0"
http-body-util = "0.1"
tower = "0.4"
futures-util = "0.3"
tokio-stream = { version = "0.1", features = ["net"] }
tokio = { version = "1.38", features = ["full"] }
http = "1.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())
})
}
}
+4
View File
@@ -8,6 +8,10 @@ use std::future::Future;
use std::task::{Context, Poll};
use http_body::Body;
pub mod generated {
pub mod helloworld;
}
pub struct RotoCodec<T, U> {
_phantom: PhantomData<(T, U)>,
}
+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();
}