Compare commits
3 Commits
fa4d8cca83
...
7e43e09f66
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e43e09f66 | |||
| 117cbf812b | |||
| 6910f11d69 |
Generated
+26
@@ -310,6 +310,12 @@ dependencies = [
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
@@ -347,6 +353,16 @@ version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-alloc"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddae17915accbac2cfbc64ea0ae6e3b330e6ea124ba108dada63646fd3c6f815"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"linked_list_allocator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "1.0.1"
|
||||
@@ -546,6 +562,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
name = "hello-world"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
@@ -776,6 +793,12 @@ version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "linked_list_allocator"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
@@ -836,6 +859,8 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
|
||||
name = "no_std_test"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"embedded-alloc",
|
||||
"roto-runtime",
|
||||
]
|
||||
|
||||
@@ -1201,6 +1226,7 @@ dependencies = [
|
||||
name = "roto-tonic"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
|
||||
+185
-47
@@ -1,13 +1,25 @@
|
||||
use crate::google::protobuf::descriptor::{
|
||||
DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, FileDescriptorProto,
|
||||
FileDescriptorSet, MessageOptions, MethodDescriptorProto, OneofDescriptorProto, ServiceDescriptorProto,
|
||||
FileDescriptorSet, MessageOptions, MethodDescriptorProto, OneofDescriptorProto,
|
||||
ServiceDescriptorProto,
|
||||
};
|
||||
use roto_runtime::ProtoAccessor;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::str;
|
||||
|
||||
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";
|
||||
const SERVICE_IMPORTS: &str = "use tonic::{Request, Response, Status};\nuse tokio_stream::Stream;\nuse std::pin::Pin;\nuse std::sync::Arc;\nuse std::task::{Context, Poll};\nuse std::future::Future;\nuse tonic::body::BoxBody;\nuse tower::Service;\nuse futures_util::StreamExt;\nuse http_body_util::BodyExt;\nuse http_body::Body;\nuse crate::{BufferPool, StatusBody};\n";
|
||||
const DATA_IMPORTS: &str = "use roto_runtime::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator, RotoMessage};\nuse core::str;\n#[cfg(feature = \"alloc\")]\nuse bytes::{Bytes, BytesMut, Buf, BufMut};\n";
|
||||
const SERVICE_IMPORTS: &str = "#[cfg(feature = \"alloc\")]\nuse tonic::{Request, Response, Status};\n\
|
||||
#[cfg(feature = \"alloc\")]\nuse tokio_stream::Stream;\n\
|
||||
#[cfg(feature = \"alloc\")]\nuse std::pin::Pin;\n\
|
||||
#[cfg(feature = \"alloc\")]\nuse std::sync::Arc;\n\
|
||||
#[cfg(feature = \"alloc\")]\nuse std::task::{Context, Poll};\n\
|
||||
#[cfg(feature = \"alloc\")]\nuse std::future::Future;\n\
|
||||
#[cfg(feature = \"alloc\")]\nuse tonic::body::BoxBody;\n\
|
||||
#[cfg(feature = \"alloc\")]\nuse tower::Service;\n\
|
||||
#[cfg(feature = \"alloc\")]\nuse futures_util::StreamExt;\n\
|
||||
#[cfg(feature = \"alloc\")]\nuse http_body_util::BodyExt;\n\
|
||||
#[cfg(feature = \"alloc\")]\nuse http_body::Body;\n\
|
||||
#[cfg(feature = \"alloc\")]\nuse crate::{BufferPool, StatusBody};\n";
|
||||
|
||||
pub fn to_pascal_case(s: &str) -> String {
|
||||
s.split('_')
|
||||
@@ -36,7 +48,11 @@ pub fn to_snake_case(s: &str) -> String {
|
||||
result
|
||||
}
|
||||
|
||||
fn map_type_to_rust_accessor(field_type: i32, label: i32, is_map: bool) -> (String, String, String) {
|
||||
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 {
|
||||
@@ -54,7 +70,7 @@ fn map_type_to_rust_accessor(field_type: i32, label: i32, is_map: bool) -> (Stri
|
||||
match field_type {
|
||||
9 => (
|
||||
"&'a str".to_string(),
|
||||
"std::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
|
||||
"core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)".to_string(),
|
||||
"\"\"".to_string(),
|
||||
), // TYPE_STRING
|
||||
1 => (
|
||||
@@ -314,7 +330,7 @@ fn write_message(msg_proto: &DescriptorProto, output: &mut String) {
|
||||
|
||||
output.push_str(&format!(
|
||||
" pub fn {}_or_default(&self) -> roto_runtime::Result<{}> {{\n",
|
||||
safe_name, rust_type
|
||||
safe_name, rust_type
|
||||
));
|
||||
output.push_str(&format!(
|
||||
" self.{}().or(Ok({}))\n",
|
||||
@@ -440,18 +456,30 @@ 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!("#[cfg(feature = \"alloc\")]\npub struct Owned{} {{\n", msg_name));
|
||||
output.push_str(&format!(
|
||||
"#[cfg(feature = \"alloc\")]\npub struct Owned{} {{\n",
|
||||
msg_name
|
||||
));
|
||||
output.push_str(" pub data: bytes::Bytes,\n");
|
||||
output.push_str("}\n\n");
|
||||
|
||||
output.push_str(&format!("#[cfg(feature = \"alloc\")]\nimpl roto_runtime::RotoOwned for Owned{} {{\n", msg_name));
|
||||
output.push_str(&format!(
|
||||
"#[cfg(feature = \"alloc\")]\nimpl 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(&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!("#[cfg(feature = \"alloc\")]\nimpl roto_runtime::RotoMessage for Owned{} {{\n", msg_name));
|
||||
output.push_str(&format!(
|
||||
"#[cfg(feature = \"alloc\")]\nimpl 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");
|
||||
@@ -551,11 +579,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
let rust_file_name = format!("{}.rs", std::path::Path::new(proto_name).file_stem().unwrap().to_str().unwrap());
|
||||
let rust_file_name = format!(
|
||||
"{}.rs",
|
||||
std::path::Path::new(proto_name)
|
||||
.file_stem()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let mut output = String::new();
|
||||
output.push_str("// @generated by protoc-gen-roto — do not edit\n");
|
||||
output.push_str("#[allow(unused_imports)]\n\n");
|
||||
output.push_str("#[allow(unused_imports)]\n");
|
||||
output.push_str(imports);
|
||||
|
||||
for dep_res in file_proto.dependency() {
|
||||
@@ -600,10 +635,12 @@ where
|
||||
}
|
||||
|
||||
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();
|
||||
if sorted_root_mods.is_empty() {
|
||||
root_mod_content.push_str("// @generated by protoc-gen-roto — do not edit\n");
|
||||
root_mod_content.push_str("#![allow(unused_imports)]\n\n");
|
||||
}
|
||||
for m in sorted_root_mods {
|
||||
root_mod_content.push_str(&format!("pub mod {};\n", m));
|
||||
}
|
||||
@@ -611,6 +648,9 @@ where
|
||||
|
||||
for (mod_path, sub_mods) in mod_files {
|
||||
let mut content = String::new();
|
||||
if sub_mods.is_empty() {
|
||||
continue;
|
||||
}
|
||||
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();
|
||||
@@ -639,7 +679,8 @@ pub fn generate_protobuf_code(
|
||||
for enum_res in file_proto.enum_type() {
|
||||
let (enum_data, _) = enum_res.expect("Failed to iterate enum");
|
||||
write_enum(
|
||||
&EnumDescriptorProto::new(enum_data).expect("Failed to parse EnumDescriptorProto"),
|
||||
&EnumDescriptorProto::new(enum_data)
|
||||
.expect("Failed to parse EnumDescriptorProto"),
|
||||
output,
|
||||
);
|
||||
}
|
||||
@@ -665,14 +706,15 @@ pub fn generate_service_code(
|
||||
set,
|
||||
files_to_generate,
|
||||
generate_mod_files,
|
||||
SERVICE_IMPORTS,
|
||||
"",
|
||||
|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"),
|
||||
&ServiceDescriptorProto::new(svc_data)
|
||||
.expect("Failed to parse ServiceDescriptorProto"),
|
||||
&package,
|
||||
output,
|
||||
);
|
||||
@@ -709,13 +751,7 @@ pub fn generate_rust_code(
|
||||
result.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
if generate_mod_files {
|
||||
let mods = generate_files_common(
|
||||
set,
|
||||
files_to_generate,
|
||||
true,
|
||||
"",
|
||||
|_, _| {},
|
||||
);
|
||||
let mods = generate_files_common(set, files_to_generate, true, "", |_, _| {});
|
||||
for (filename, content) in mods {
|
||||
if filename == "mod.rs" || filename.contains("/mod.rs") {
|
||||
result.push((filename, content));
|
||||
@@ -727,25 +763,81 @@ pub fn generate_rust_code(
|
||||
}
|
||||
|
||||
fn strip_boilerplate(content: &str) -> String {
|
||||
// Find the first occurrence of a service definition or a trait
|
||||
// In our case, the services start after the dependency imports and a newline.
|
||||
if let Some(idx) = content.find("pub trait ") {
|
||||
return content[idx..].to_string();
|
||||
let mut stripped = content;
|
||||
let header = "// @generated by protoc-gen-roto — do not edit\n";
|
||||
if stripped.starts_with(header) {
|
||||
stripped = &stripped[header.len()..];
|
||||
|
||||
let patterns = [
|
||||
"#[allow(unused_imports)]\n",
|
||||
"#![allow(unused_imports)]\n\n",
|
||||
"#![allow(unused_imports)]\n",
|
||||
];
|
||||
|
||||
for pattern in patterns {
|
||||
if stripped.starts_with(pattern) {
|
||||
stripped = &stripped[pattern.len()..];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(idx) = content.find("pub struct ") {
|
||||
// This might be a message, but generate_service_code only generates services (and their server structs)
|
||||
return content[idx..].to_string();
|
||||
stripped.to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_strip_boilerplate_standard() {
|
||||
let content = "// @generated by protoc-gen-roto — do not edit\n#[allow(unused_imports)]\nuse std::str;\n";
|
||||
let stripped = strip_boilerplate(content);
|
||||
assert_eq!(stripped, "use std::str;\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strip_boilerplate_crate_level() {
|
||||
let content = "// @generated by protoc-gen-roto — do not edit\n#![allow(unused_imports)]\n\nuse std::str;\n";
|
||||
let stripped = strip_boilerplate(content);
|
||||
assert_eq!(stripped, "use std::str;\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strip_boilerplate_crate_level_single_newline() {
|
||||
let content = "// @generated by protoc-gen-roto — do not edit\n#![allow(unused_imports)]\nuse std::str;\n";
|
||||
let stripped = strip_boilerplate(content);
|
||||
assert_eq!(stripped, "use std::str;\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strip_boilerplate_no_boilerplate() {
|
||||
let content = "use std::str;\n";
|
||||
let stripped = strip_boilerplate(content);
|
||||
assert_eq!(stripped, "use std::str;\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strip_boilerplate_only_header() {
|
||||
let content = "// @generated by protoc-gen-roto — do not edit\n";
|
||||
let stripped = strip_boilerplate(content);
|
||||
assert_eq!(stripped, "");
|
||||
}
|
||||
content.to_string()
|
||||
}
|
||||
|
||||
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!("#[tonic::async_trait]\npub trait {}: Send + Sync + 'static {{\n", svc_name));
|
||||
output.push_str("#[cfg(feature = \"alloc\")]\n");
|
||||
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_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();
|
||||
@@ -767,7 +859,10 @@ fn write_service(svc_proto: &ServiceDescriptorProto, package: &str, output: &mut
|
||||
};
|
||||
|
||||
let resp_type = if server_streaming {
|
||||
format!("Response<Pin<Box<dyn Stream<Item = std::result::Result<{}, Status>> + Send>>>", output_owned)
|
||||
format!(
|
||||
"Response<Pin<Box<dyn Stream<Item = std::result::Result<{}, Status>> + Send>>>",
|
||||
output_owned
|
||||
)
|
||||
} else {
|
||||
format!("Response<{}>", output_owned)
|
||||
};
|
||||
@@ -780,27 +875,46 @@ fn write_service(svc_proto: &ServiceDescriptorProto, package: &str, output: &mut
|
||||
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("#[cfg(feature = \"alloc\")]\n");
|
||||
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("#[cfg(feature = \"alloc\")]\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(&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));
|
||||
output.push_str("#[cfg(feature = \"alloc\")]\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(&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("#[cfg(feature = \"alloc\")]\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");
|
||||
@@ -837,14 +951,20 @@ fn write_service(svc_proto: &ServiceDescriptorProto, package: &str, output: &mut
|
||||
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 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));
|
||||
methods.push((
|
||||
original_method_name,
|
||||
method_name,
|
||||
input_owned,
|
||||
server_streaming,
|
||||
));
|
||||
}
|
||||
|
||||
for (original_method_name, method_name, input_owned, server_streaming) in methods {
|
||||
@@ -854,7 +974,12 @@ fn write_service(svc_proto: &ServiceDescriptorProto, package: &str, output: &mut
|
||||
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)
|
||||
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");
|
||||
@@ -865,17 +990,28 @@ fn write_service(svc_proto: &ServiceDescriptorProto, package: &str, output: &mut
|
||||
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)
|
||||
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(&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(&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");
|
||||
@@ -892,7 +1028,9 @@ fn write_service(svc_proto: &ServiceDescriptorProto, package: &str, output: &mut
|
||||
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(
|
||||
" 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");
|
||||
|
||||
@@ -24,7 +24,12 @@ 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 = []
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target = "thumbv7em-none-eabihf"
|
||||
@@ -5,6 +5,11 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
roto-runtime = { path = "../../runtime", default-features = false }
|
||||
embedded-alloc = { version = "0.5", optional = true }
|
||||
bytes = { version = "1.7", default-features = false }
|
||||
|
||||
[features]
|
||||
alloc = ["roto-runtime/alloc", "embedded-alloc"]
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
@@ -0,0 +1,677 @@
|
||||
// @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;
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,74 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![cfg_attr(not(test), no_main)]
|
||||
|
||||
use roto_runtime::ProtoAccessor;
|
||||
mod helloworld;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
use roto_runtime::{ProtoAccessor, RotoMessage, RotoOwned};
|
||||
|
||||
#[cfg(all(feature = "alloc", not(test)))]
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: embedded_alloc::Heap = embedded_alloc::Heap::empty();
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _critical_section_1_0_acquire() {}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _critical_section_1_0_release() {}
|
||||
|
||||
static HELLO_DATA: &[u8] = &[0x0A, 0x05, 0x57, 0x6f, 0x72, 0x6c, 0x64];
|
||||
|
||||
#[cfg(all(not(test), not(feature = "alloc")))]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
let _data = [0u8; 0];
|
||||
let _ = ProtoAccessor::new(&_data);
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
{
|
||||
let hello = helloworld::Hello::new(HELLO_DATA).expect("failed to decode hello");
|
||||
let _name = hello.name().expect("failed to get name");
|
||||
if !_name.is_empty() {
|
||||
// Valid
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
{
|
||||
use embedded_alloc::Heap;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
static mut HEAP: Heap = Heap::empty();
|
||||
unsafe {
|
||||
core::ptr::addr_of_mut!(HEAP).write(embedded_alloc::Heap::empty());
|
||||
(*core::ptr::addr_of_mut!(HEAP)).init(MaybeUninit::<u8>::uninit().as_ptr() as *mut u8 as usize, 1024 * 1024);
|
||||
let owned_hello = helloworld::OwnedHello::decode(HELLO_DATA.into()).expect("failed to decode owned hello");
|
||||
let hello_reader = owned_hello.reader();
|
||||
let _name = hello_reader.name().expect("failed to get name");
|
||||
if !_name.is_empty() {
|
||||
// Valid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_helloworld_decoding() {
|
||||
let hello = helloworld::Hello::new(HELLO_DATA).expect("failed to decode hello");
|
||||
let name = hello.name().expect("failed to get name");
|
||||
assert!(!name.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ You are the Researcher. Your role is to conduct a comprehensive initial analysis
|
||||
|
||||
Your workflow:
|
||||
1. Analyze the problem statement to identify key technical requirements and unknown areas.
|
||||
2. Use available tools (such as SearXNG and web search) to research existing libraries, frameworks, APIs, and best practices relevant to the problem.
|
||||
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.
|
||||
|
||||
@@ -12,9 +12,14 @@ 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 = []
|
||||
|
||||
@@ -1,22 +1,9 @@
|
||||
// @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 core::str;
|
||||
#[cfg(feature = "alloc")]
|
||||
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 UnaryRequest<'a> {
|
||||
accessor: roto_runtime::ProtoAccessor<'a>,
|
||||
@@ -41,7 +28,7 @@ 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)?;
|
||||
std::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
|
||||
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
|
||||
}
|
||||
|
||||
pub fn message_or_default(&self) -> roto_runtime::Result<&'a str> {
|
||||
@@ -94,10 +81,12 @@ impl<'b> UnaryRequestBuilder<'b> {
|
||||
}
|
||||
}
|
||||
|
||||
#[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<'_> {
|
||||
@@ -105,6 +94,7 @@ impl roto_runtime::RotoOwned for OwnedUnaryRequest {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl roto_runtime::RotoMessage for OwnedUnaryRequest {
|
||||
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
|
||||
Ok(OwnedUnaryRequest { data: buf })
|
||||
@@ -138,7 +128,7 @@ 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)?;
|
||||
std::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
|
||||
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
|
||||
}
|
||||
|
||||
pub fn reply_or_default(&self) -> roto_runtime::Result<&'a str> {
|
||||
@@ -191,10 +181,12 @@ impl<'b> UnaryResponseBuilder<'b> {
|
||||
}
|
||||
}
|
||||
|
||||
#[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<'_> {
|
||||
@@ -202,6 +194,7 @@ impl roto_runtime::RotoOwned for OwnedUnaryResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl roto_runtime::RotoMessage for OwnedUnaryResponse {
|
||||
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
|
||||
Ok(OwnedUnaryResponse { data: buf })
|
||||
@@ -235,7 +228,7 @@ 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)?;
|
||||
std::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
|
||||
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
|
||||
}
|
||||
|
||||
pub fn query_or_default(&self) -> roto_runtime::Result<&'a str> {
|
||||
@@ -288,10 +281,12 @@ impl<'b> StreamingRequestBuilder<'b> {
|
||||
}
|
||||
}
|
||||
|
||||
#[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<'_> {
|
||||
@@ -299,6 +294,7 @@ impl roto_runtime::RotoOwned for OwnedStreamingRequest {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl roto_runtime::RotoMessage for OwnedStreamingRequest {
|
||||
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
|
||||
Ok(OwnedStreamingRequest { data: buf })
|
||||
@@ -332,7 +328,7 @@ 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)?;
|
||||
std::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
|
||||
core::str::from_utf8(bytes).map_err(|_| roto_runtime::RotoError::WireFormatViolation)
|
||||
}
|
||||
|
||||
pub fn item_or_default(&self) -> roto_runtime::Result<&'a str> {
|
||||
@@ -385,10 +381,12 @@ impl<'b> StreamingResponseBuilder<'b> {
|
||||
}
|
||||
}
|
||||
|
||||
#[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<'_> {
|
||||
@@ -396,6 +394,7 @@ impl roto_runtime::RotoOwned for OwnedStreamingResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl roto_runtime::RotoMessage for OwnedStreamingResponse {
|
||||
fn decode(buf: bytes::Bytes) -> roto_runtime::Result<Self> {
|
||||
Ok(OwnedStreamingResponse { data: buf })
|
||||
@@ -406,28 +405,60 @@ impl roto_runtime::RotoMessage for OwnedStreamingResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
|
||||
|
||||
#[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;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#![no_std]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate std;
|
||||
|
||||
@@ -438,6 +441,8 @@ impl<'a> Iterator for RawFieldIterator<'a> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::{vec, vec::{Vec}};
|
||||
|
||||
#[test]
|
||||
fn test_varint_read_write() {
|
||||
|
||||
Reference in New Issue
Block a user