Compare commits

..

11 Commits

Author SHA1 Message Date
charles 858c6968d4 Clean warnings 2026-05-04 11:14:57 -07:00
charles 7fbea70860 Migrate to using generated code 2026-05-04 10:45:38 -07:00
charles a4fa72c819 Include generated code 2026-05-04 09:23:51 -07:00
charles 22c4e17e9f Fix code generation 2026-05-04 09:23:01 -07:00
charles 87b111faf5 Code compiles, but tests fail 2026-05-04 09:04:28 -07:00
charles d9be36726f Implement protobuf enum and message generation
Add `write_enum` and `write_message` to generate Rust enums and structs
from protobuf descriptors. Update the generator tests to support the
updated `generate_rust_code` signature.
2026-05-04 08:59:23 -07:00
charles 74d975788b add: files 2026-05-03 21:03:55 -07:00
charles 20e4fb909b Generate modules to make easy importing 2026-05-03 20:44:07 -07:00
charles a2a5c12235 Escape 'type' keyword in generated Rust code
Use raw identifiers for fields named "type" to avoid conflicts with the
Rust keyword. Fix field number indexing in tests.
2026-05-03 14:00:20 -07:00
charles b73cbb3dbc Merge branch 'add-generated-markers' and resolve conflicts in src/generator.rs 2026-05-03 13:45:25 -07:00
charles 38367227ed Cache field offsets in generated accessors
Update the generator to store field offsets and ranges within the
generated accessor structs, avoiding repeated buffer scans.
2026-05-03 13:32:39 -07:00
16 changed files with 5546 additions and 3398 deletions
+19
View File
@@ -0,0 +1,19 @@
## Ask for clarification
Unless the request is extremely clear, assemble a list of questions up front. After you begin work,
you should be able to work without user assistance.
## Coding
If you are writing code, write tests first. The tests must pass for your work to be complete.
## Special instructions
### Fork
If the users asks you to fork off and work on something, this means that you should:
1. Create a temporary directory using `mktemp -d`
2. Create a new branch to work on
3. Use git worktree to extract that branch to the temporary directory (i.e., `git worktree add $TMP_DIR -- $BRANCH)
4. Work from that directory (i.e., `cd $TMP_DIR`)
+16 -4
View File
@@ -1,5 +1,7 @@
use clap::Parser; use clap::Parser;
use roto::proto_gen::google::protobuf::descriptor::FileDescriptorSet; use roto::google::protobuf::descriptor::{
FileDescriptorSet
};
use roto::generator::generate_rust_code; use roto::generator::generate_rust_code;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
@@ -11,9 +13,13 @@ struct Args {
#[arg(short, long)] #[arg(short, long)]
input: PathBuf, input: PathBuf,
/// Path to the output Rust file (.rs) /// Path to the output directory
#[arg(short, long)] #[arg(short, long)]
output: PathBuf, output: PathBuf,
/// Files to generate. If omitted, all files are generated.
#[arg(short, long, value_delimiter = ',')]
files: Option<Vec<String>>,
} }
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -21,8 +27,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let data = fs::read(&args.input)?; let data = fs::read(&args.input)?;
let set = FileDescriptorSet::new(&data).expect("Failed to parse FileDescriptorSet"); let set = FileDescriptorSet::new(&data).expect("Failed to parse FileDescriptorSet");
let output = generate_rust_code(&set); let files = generate_rust_code(&set, args.files.as_deref(), true);
fs::write(&args.output, output)?; for (filename, content) in files {
let path = args.output.join(filename);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(path, content)?;
}
Ok(()) Ok(())
} }
+22 -20
View File
@@ -1,10 +1,13 @@
use env_logger::init; use env_logger::init;
use log::{error, info}; use log::{error, info};
use roto::generator::generate_rust_code; use roto::generator::generate_rust_code;
use roto::proto_gen::google::protobuf::descriptor::{ use roto::google::protobuf::descriptor::{
CodeGeneratorRequest, CodeGeneratorResponse, FileDescriptorSet, ResponseFile, FileDescriptorSet
}; };
use roto::ProtoBuilder; use roto::google::protobuf::compiler::plugin::{
CodeGeneratorRequest, CodeGeneratorResponseBuilder, code_generator_response::FileBuilder,
};
// use roto::ProtoBuilder;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
fn main() { fn main() {
@@ -67,36 +70,35 @@ fn handle_request(request: &CodeGeneratorRequest) -> std::result::Result<Vec<u8>
let set = FileDescriptorSet::new(&set_buf)?; let set = FileDescriptorSet::new(&set_buf)?;
let files_to_generate: Vec<String> = request.file_to_generate()
.filter_map(|res| {
let (bytes, _) = res.ok()?;
std::str::from_utf8(bytes).ok().map(|s| s.to_string())
})
.collect();
// Generate the Rust code // Generate the Rust code
info!("Generating Rust code from descriptor set..."); info!("Generating Rust code from descriptor set...");
let generated_code = generate_rust_code(&set); let generated_files = generate_rust_code(&set, Some(&files_to_generate), false);
// Determine the output filename
let mut output_filename = "roto_generated.rs".to_string();
if let Some(first_file) = request.file_to_generate().next() {
if let Ok((name_bytes, _)) = first_file {
if let Ok(name) = std::str::from_utf8(name_bytes) {
output_filename = format!("{}.rs", name.replace(".proto", ""));
}
}
}
// Construct the response // Construct the response
let mut response_buf = vec![0u8; 1024 * 1024 * 2]; // Allocate 2MB for response let mut response_buf = vec![0u8; 1024 * 1024 * 2]; // Allocate 2MB for response
let mut resp_builder = CodeGeneratorResponse::builder(&mut response_buf); let mut resp_builder = CodeGeneratorResponseBuilder::builder(&mut response_buf);
for (filename, content) in generated_files {
let mut file_buf = vec![0u8; 1024 * 1024 * 2]; let mut file_buf = vec![0u8; 1024 * 1024 * 2];
let final_file = ResponseFile::builder(&mut file_buf) let final_file = FileBuilder::builder(&mut file_buf)
.name(&output_filename)? .name(&filename)?
.content(&generated_code)? .content(&content)?
.finish() .finish()
.map_err(|e| { .map_err(|e| {
error!("Failed to build ResponseFile: {:?}", e); error!("Failed to build ResponseFile {}: {:?}", filename, e);
e e
})?; })?;
resp_builder = resp_builder.file(final_file)?;
}
let final_response_slice = resp_builder let final_response_slice = resp_builder
.add_file(final_file)?
.finish() .finish()
.map_err(|e| { .map_err(|e| {
error!("Failed to finish CodeGeneratorResponse: {:?}", e); error!("Failed to finish CodeGeneratorResponse: {:?}", e);
+301 -133
View File
@@ -1,8 +1,9 @@
use crate::proto_gen::google::protobuf::descriptor::{ use crate::google::protobuf::descriptor::{
DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, FileDescriptorProto, FileDescriptorSet, DescriptorProto, EnumDescriptorProto, FileDescriptorProto, FieldDescriptorProto, FileDescriptorSet
}; };
use crate::{ProtoAccessor, Result, RotoError}; use crate::ProtoAccessor;
use std::str; use std::str;
use std::collections::{HashMap, HashSet};
pub fn to_pascal_case(s: &str) -> String { pub fn to_pascal_case(s: &str) -> String {
s.split('_') s.split('_')
@@ -16,53 +17,267 @@ pub fn to_pascal_case(s: &str) -> String {
.collect() .collect()
} }
pub fn to_snake_case(s: &str) -> String {
let mut result = String::new();
for (i, c) in s.chars().enumerate() {
if c.is_uppercase() {
if i > 0 {
result.push('_');
}
result.push(c.to_ascii_lowercase());
} else {
result.push(c);
}
}
result
}
fn map_type_to_rust_accessor(field_type: i32, label: i32) -> (String, String) { fn map_type_to_rust_accessor(field_type: i32, label: i32) -> (String, String) {
if label == 3 { if label == 3 {
// LABEL_REPEATED // LABEL_REPEATED
return ( return (
"crate::RepeatedFieldIterator<'a>".to_string(), "crate::RepeatedFieldIterator<'a>".to_string(),
"self.0.iter_repeated(%d)".to_string(), "".to_string(), // Not used for repeated fields in the same way
); );
} }
match field_type { match field_type {
9 => ( 9 => (
"&'a str".to_string(), "&'a str".to_string(),
"str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)".to_string(), "str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation)".to_string(),
), // TYPE_STRING ), // TYPE_STRING
1 => ( 1 => (
"f64".to_string(), "f64".to_string(),
"Ok(f64::from_le_bytes(bytes.try_into().map_err(|_| RotoError::WireFormatViolation)?))".to_string(), "Ok(f64::from_le_bytes(bytes.try_into().map_err(|_| crate::RotoError::WireFormatViolation)?))".to_string(),
), // TYPE_DOUBLE ), // TYPE_DOUBLE
2 => ( 2 => (
"f32".to_string(), "f32".to_string(),
"f32::from_le_bytes(bytes.try_into().map_err(|_| RotoError::WireFormatViolation)?)".to_string(), "f32::from_le_bytes(bytes.try_into().map_err(|_| crate::RotoError::WireFormatViolation)?)".to_string(),
), // TYPE_FLOAT ), // TYPE_FLOAT
3 | 5 | 15 | 17 => ( 3 | 5 | 15 | 17 => (
"i32".to_string(), "i32".to_string(),
"crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| RotoError::WireFormatViolation)".to_string(), "crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation)".to_string(),
), // INT/SINT/SFIXED 32 ), // INT/SINT/SFIXED 32
4 | 6 | 13 => ( 4 | 6 | 13 => (
"u32".to_string(), "u32".to_string(),
"crate::read_varint(bytes).map(|(v, _)| v as u32).map_err(|_| RotoError::WireFormatViolation)".to_string(), "crate::read_varint(bytes).map(|(v, _)| v as u32).map_err(|_| crate::RotoError::WireFormatViolation)".to_string(),
), // UINT/FIXED 32 ), // UINT/FIXED 32
16 | 18 => ( 16 | 18 => (
"i64".to_string(), "i64".to_string(),
"crate::read_varint(bytes).map(|(v, _)| v as i64).map_err(|_| RotoError::WireFormatViolation)".to_string(), "crate::read_varint(bytes).map(|(v, _)| v as i64).map_err(|_| crate::RotoError::WireFormatViolation)".to_string(),
), // SINT/SFIXED 64 ), // SINT/SFIXED 64
7 | 14 => ( 7 | 14 => (
"u64".to_string(), "u64".to_string(),
"crate::read_varint(bytes).map(|(v, _)| v as u64).map_err(|_| RotoError::WireFormatViolation)".to_string(), "crate::read_varint(bytes).map(|(v, _)| v as u64).map_err(|_| crate::RotoError::WireFormatViolation)".to_string(),
), // UINT/FIXED 64 ), // UINT/FIXED 64
8 => ( 8 => (
"bool".to_string(), "bool".to_string(),
"crate::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| RotoError::WireFormatViolation)".to_string(), "crate::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| crate::RotoError::WireFormatViolation)".to_string(),
), // TYPE_BOOL ), // TYPE_BOOL
11 | 12 => ("&'a [u8]".to_string(), "Ok(bytes)".to_string()), // MESSAGE/BYTES 11 | 12 => ("&'a [u8]".to_string(), "Ok(bytes)".to_string()), // MESSAGE/BYTES
_ => ("&'a [u8]".to_string(), "Ok(bytes)".to_string()), _ => ("&'a [u8]".to_string(), "Ok(bytes)".to_string()),
} }
} }
fn write_enum(enum_proto: &EnumDescriptorProto, output: &mut String) {
let enum_name = to_pascal_case(enum_proto.name().unwrap());
output.push_str(&format!(
"#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[repr(i32)]\npub enum {} {{\n",
enum_name
));
let mut values = enum_proto.value();
let mut zero_variant_name = None;
while let Some(val_res) = values.next() {
let (val_data, _) = val_res.expect("Failed to iterate enum");
let accessor = ProtoAccessor::new(val_data).expect("Failed to parse EnumValueDescriptorProto");
let (name_bytes, _) = accessor.get_value(1).expect("Enum value name missing");
let name = str::from_utf8(name_bytes).expect("Enum value name invalid utf8");
let (num_bytes, _) = accessor.get_value(2).expect("Enum value number missing");
let (num, _) = crate::read_varint(num_bytes).expect("Enum value number invalid varint");
let pascal_name = to_pascal_case(name);
if num == 0 {
zero_variant_name = Some(pascal_name.clone());
}
output.push_str(&format!(" {} = {},\n", pascal_name, num));
}
if zero_variant_name.is_none() {
output.push_str(" Unknown = 0,\n");
zero_variant_name = Some("Unknown".to_string());
}
output.push_str("}\n\n");
output.push_str(&format!(
"impl {} {{\n pub fn from_i32(value: i32) -> Self {{\n match value {{\n",
enum_name
));
let mut values = enum_proto.value();
while let Some(val_res) = values.next() {
let (val_data, _) = val_res.expect("Failed to read enum value");
let accessor = ProtoAccessor::new(val_data).expect("Failed to parse EnumValueDescriptorProto");
let (name_bytes, _) = accessor.get_value(1).expect("Enum value name missing");
let name = str::from_utf8(name_bytes).expect("Enum value name invalid utf8");
let (num_bytes, _) = accessor.get_value(2).expect("Enum value number missing");
let (num, _) = crate::read_varint(num_bytes).expect("Enum value number invalid varint");
output.push_str(&format!(" {} => {}::{},\n", num, enum_name, to_pascal_case(name)));
}
output.push_str(&format!(" _ => {}::{},\n", enum_name, zero_variant_name.as_ref().unwrap()));
output.push_str(" }\n }\n}\n\n");
}
fn write_message(msg_proto: &DescriptorProto, output: &mut String) {
let msg_name = to_pascal_case(msg_proto.name().unwrap());
let mut fields_info = Vec::new();
for field_res in msg_proto.field() {
let (field_data, _) = field_res.expect("Failed to iterate field");
let field_proto = FieldDescriptorProto::new(field_data).expect("Failed to parse FieldDescriptorProto");
let field_name = field_proto.name().unwrap();
let tag = field_proto.number().unwrap();
let f_type = field_proto.r#type().unwrap() as i32;
let f_label = field_proto.label().unwrap() as i32;
fields_info.push((field_name.to_string(), tag, f_type, f_label));
}
output.push_str(&format!(
"pub struct {}<'a> {{\n",
msg_name
));
if !fields_info.is_empty() {
output.push_str(" accessor: crate::ProtoAccessor<'a>,\n");
}
for (field_name, _tag, _f_type, f_label) in &fields_info {
if *f_label == 3 {
output.push_str(&format!(" {}_start: Option<usize>,\n", field_name));
output.push_str(&format!(" {}_end: Option<usize>,\n", field_name));
} else {
output.push_str(&format!(" {}_offset: Option<usize>,\n", field_name));
}
}
output.push_str("}\n\n");
output.push_str(&format!("impl<'a> {}<'a> {{\n", msg_name));
output.push_str(" pub fn new(data: &'a [u8]) -> crate::Result<Self> {\n");
if !fields_info.is_empty() {
output.push_str(" let accessor = crate::ProtoAccessor::new(data)?;\n");
for (name, _, _, label) in &fields_info {
if *label == 3 {
output.push_str(&format!(" let mut {}_start = None;\n", name));
output.push_str(&format!(" let mut {}_end = None;\n", name));
} else {
output.push_str(&format!(" let mut {}_offset = None;\n", name));
}
}
output.push_str(" for item in accessor.fields() {\n");
output.push_str(" let (offset, tag, _) = item?;\n");
for (name, tag, _, label) in &fields_info {
if *label == 3 {
output.push_str(&format!(" if tag.field_number == {} {{\n", tag));
output.push_str(&format!(" if {}_start.is_none() {{ {}_start = Some(offset); }}\n", name, name));
output.push_str(&format!(" {}_end = Some(offset);\n", name));
output.push_str(" }\n");
} else {
output.push_str(&format!(" if tag.field_number == {} {{ {}_offset = Some(offset); }}\n", tag, name));
}
}
output.push_str(" }\n\n");
} else {
output.push_str(" let _ = crate::ProtoAccessor::new(data)?;\n");
}
output.push_str(" Ok(Self {\n");
if !fields_info.is_empty() {
output.push_str(" accessor,\n");
}
for (name, _, _, label) in &fields_info {
if *label == 3 {
output.push_str(&format!("{}_start, {}_end,\n", name, name));
} else {
output.push_str(&format!("{}_offset,\n", name));
}
}
output.push_str(" })\n }\n\n");
for (field_name, tag, f_type, f_label) in fields_info {
let (rust_type, logic) = map_type_to_rust_accessor(f_type, f_label);
let safe_name = if field_name == "type" { format!("r#{}", field_name) } else { field_name.clone() };
if f_label == 3 {
output.push_str(&format!(" pub fn {}(&self) -> {} {{\n", safe_name, rust_type));
output.push_str(&format!(" match (self.{}_start, self.{}_end) {{\n", field_name, field_name));
output.push_str(&format!(" (Some(start), Some(end)) => self.accessor.iter_repeated_range({}, start, end),\n", tag));
output.push_str(&format!(" _ => self.accessor.iter_repeated({}),\n", tag));
output.push_str(" }\n }\n\n");
} else {
output.push_str(&format!(" pub fn {}(&self) -> crate::Result<{}> {{\n", safe_name, rust_type));
output.push_str(&format!(" let offset = self.{}_offset.ok_or(crate::RotoError::FieldNotFound)?;\n", field_name));
output.push_str(" let (bytes, _) = self.accessor.get_value_at(offset)?;\n");
output.push_str(&format!(" {}\n", logic));
output.push_str(" }\n\n");
}
}
output.push_str("}\n\n");
// Builder
output.push_str(&format!(
"pub struct {}Builder<'b> {{\n builder: crate::ProtoBuilder<'b>,\n}}\n\nimpl<'b> {}Builder<'b> {{\n",
msg_name, msg_name
));
output.push_str(&format!(
" pub fn builder(buf: &mut [u8]) -> {}Builder<'_> {{\n {}Builder {{\n builder: crate::ProtoBuilder::new(buf),\n }}\n }}\n\n",
msg_name, msg_name
));
for field_res in msg_proto.field() {
let (field_data, _) = field_res.expect("Failed to iterate field");
let field_proto = FieldDescriptorProto::new(field_data).expect("Failed to parse FieldDescriptorProto");
let field_name = field_proto.name().unwrap();
let safe_name = if field_name == "type" { format!("r#{}", field_name) } else { field_name.to_string() };
let tag = field_proto.number().unwrap();
let f_type = field_proto.r#type().unwrap() as i32;
let (rust_type, method) = map_type_to_rust_builder(f_type);
output.push_str(&format!(
" pub fn {}(mut self, value: {}) -> crate::Result<Self> {{\n self.builder.{}({}, value)?;\n Ok(self)\n }}\n\n",
safe_name, rust_type, method, tag
));
}
output.push_str(&format!(" pub fn finish(self) -> crate::Result<&'b mut [u8]> {{\n self.builder.finish()\n }}\n}}\n\n"));
let mut nested_enums = Vec::new();
for e_res in msg_proto.enum_type() {
if let Ok((e, _)) = e_res { nested_enums.push(e); }
}
let mut nested_msgs = Vec::new();
for m_res in msg_proto.nested_type() {
if let Ok((m, _)) = m_res { nested_msgs.push(m); }
}
if !nested_enums.is_empty() || !nested_msgs.is_empty() {
let mod_name = to_snake_case(msg_proto.name().unwrap());
output.push_str(&format!("pub mod {} {{\n", mod_name));
for e_data in nested_enums {
write_enum(&EnumDescriptorProto::new(e_data).expect("Failed to parse nested EnumDescriptorProto"), output);
}
for m_data in nested_msgs {
write_message(&DescriptorProto::new(m_data).expect("Failed to parse nested DescriptorProto"), output);
}
output.push_str("}\n\n");
}
}
fn map_type_to_rust_builder(field_type: i32) -> (String, String) { fn map_type_to_rust_builder(field_type: i32) -> (String, String) {
match field_type { match field_type {
9 => ("&str".to_string(), "write_string".to_string()), 9 => ("&str".to_string(), "write_string".to_string()),
@@ -75,144 +290,97 @@ fn map_type_to_rust_builder(field_type: i32) -> (String, String) {
} }
} }
pub fn generate_rust_code(set: &FileDescriptorSet) -> String { pub fn generate_rust_code(
let mut output = String::new(); set: &FileDescriptorSet,
output.push_str("use crate::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator};\n"); files_to_generate: Option<&[String]>,
output.push_str("use std::str;\n\n"); generate_mod_files: bool,
) -> Vec<(String, String)> {
let mut generated_files = Vec::new();
for file_res in set.file() { for file_res in set.file() {
let (file_data, _) = file_res.expect("Failed to iterate file"); let (file_data, _) = file_res.expect("Failed to iterate file");
let file_proto = FileDescriptorProto::new(file_data).expect("Failed to parse FileDescriptorProto"); let file_proto = FileDescriptorProto::new(file_data).expect("Failed to parse FileDescriptorProto");
let proto_name = file_proto.name().expect("File proto name missing");
if let Some(filter) = files_to_generate {
if !filter.contains(&proto_name.to_string()) {
continue;
}
}
let rust_file_name = format!("{}.rs", proto_name.replace(".proto", ""));
let mut output = String::new();
output.push_str("use crate::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator};\n");
output.push_str("use std::str;\n\n");
for dep_res in file_proto.dependency() {
let (dep_data, _) = dep_res.expect("Failed to iterate dependency");
let dep_name = str::from_utf8(dep_data).expect("Dependency name invalid utf8");
let dep_mod_path = dep_name.replace(".proto", "").replace('/', "::");
output.push_str(&format!("use crate::{};\n", dep_mod_path));
}
output.push_str("\n");
// Enums // Enums
for enum_res in file_proto.enum_type() { for enum_res in file_proto.enum_type() {
let (enum_data, _) = enum_res.expect("Failed to iterate enum"); let (enum_data, _) = enum_res.expect("Failed to iterate enum");
let enum_proto = EnumDescriptorProto::new(enum_data).expect("Failed to parse EnumDescriptorProto"); write_enum(&EnumDescriptorProto::new(enum_data).expect("Failed to parse EnumDescriptorProto"), &mut output);
let enum_name = to_pascal_case(enum_proto.name().unwrap());
output.push_str(&format!(
"#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[repr(i32)]\npub enum {} {{\n",
enum_name
));
let mut values = enum_proto.enum_value();
let mut variant_count = 0;
let mut zero_variant_name = None;
while let Some(val_res) = values.next() {
let (val_data, _) = val_res.expect("Failed to iterate enum");
let accessor = ProtoAccessor::new(val_data).expect("Failed to parse EnumValueDescriptorProto");
let (name_bytes, _) = accessor.get_value(1).expect("Enum value name missing");
let name = str::from_utf8(name_bytes).expect("Enum value name invalid utf8");
let (num_bytes, _) = accessor.get_value(2).expect("Enum value number missing");
let (num, _) = crate::read_varint(num_bytes).expect("Enum value number invalid varint");
let pascal_name = to_pascal_case(name);
if num == 0 {
zero_variant_name = Some(pascal_name.clone());
}
output.push_str(&format!(" {} = {},\n", pascal_name, num));
variant_count += 1;
}
if zero_variant_name.is_none() {
output.push_str(" Unknown = 0,\n");
zero_variant_name = Some("Unknown".to_string());
}
output.push_str("}\n\n");
output.push_str(&format!(
"impl {} {{\n pub fn from_i32(value: i32) -> Self {{\n match value {{\n",
enum_name
));
let mut values = enum_proto.enum_value();
while let Some(val_res) = values.next() {
let (val_data, _) = val_res.expect("Failed to read enum value");
let accessor = ProtoAccessor::new(val_data).expect("Failed to parse EnumValueDescriptorProto");
let (name_bytes, _) = accessor.get_value(1).expect("Enum value name missing");
let name = str::from_utf8(name_bytes).expect("Enum value name invalid utf8");
let (num_bytes, _) = accessor.get_value(2).expect("Enum value number missing");
let (num, _) = crate::read_varint(num_bytes).expect("Enum value number invalid varint");
output.push_str(&format!(" {} => {}::{},\n", num, enum_name, to_pascal_case(name)));
}
output.push_str(&format!(" _ => {}::{},\n", enum_name, zero_variant_name.as_ref().unwrap()));
output.push_str(" }\n }\n}\n\n");
} }
// Messages // Messages
for msg_res in file_proto.message_type() { for msg_res in file_proto.message_type() {
let (msg_data, _) = msg_res.expect("Failed to iterate message"); let (msg_data, _) = msg_res.expect("Failed to iterate message");
let msg_proto = DescriptorProto::new(msg_data).expect("Failed to parse DescriptorProto"); write_message(&DescriptorProto::new(msg_data).expect("Failed to parse DescriptorProto"), &mut output);
let msg_name = to_pascal_case(msg_proto.name().unwrap());
// Accessor
output.push_str(&format!(
"pub struct {}<'a>(ProtoAccessor<'a>);\n\nimpl<'a> {}<'a> {{\n",
msg_name, msg_name
));
output.push_str(&format!(
" pub fn new(data: &'a [u8]) -> Result<Self> {{\n Ok(Self(ProtoAccessor::new(data)?))\n }}\n\n"
));
for field_res in msg_proto.field() {
let (field_data, _) = field_res.expect("Failed to iterate field");
let field_proto = FieldDescriptorProto::new(field_data).expect("Failed to parse FieldDescriptorProto");
let field_name = field_proto.name().unwrap();
let safe_name = if field_name == "type" { format!("r#{}", field_name) } else { field_name.to_string() };
let tag = field_proto.number().unwrap();
let f_type = field_proto.field_type().unwrap() as i32;
let f_label = field_proto.label().unwrap() as i32;
let (rust_type, logic) = map_type_to_rust_accessor(f_type, f_label);
if f_label == 3 {
output.push_str(&format!(
" pub fn {}(&self) -> {} {{\n {}\n }}\n\n",
safe_name, rust_type, logic.replace("%d", &tag.to_string())
));
} else {
output.push_str(&format!(
" pub fn {}(&self) -> Result<{}> {{\n let (bytes, _) = self.0.get_value({})?;\n {}\n }}\n\n",
safe_name, rust_type, tag, logic
));
} }
} generated_files.push((rust_file_name, output));
output.push_str("}\n\n");
// Builder
output.push_str(&format!(
"pub struct {}Builder<'b> {{\n builder: ProtoBuilder<'b>,\n}}\n\nimpl<'b> {}Builder<'b> {{\n",
msg_name, msg_name
));
output.push_str(&format!(
" pub fn builder(buf: &mut [u8]) -> {}Builder<'_> {{\n {}Builder {{\n builder: ProtoBuilder::new(buf),\n }}\n }}\n\n",
msg_name, msg_name
));
for field_res in msg_proto.field() {
let (field_data, _) = field_res.expect("Failed to iterate field");
let field_proto = FieldDescriptorProto::new(field_data).expect("Failed to parse FieldDescriptorProto");
let field_name = field_proto.name().unwrap();
let safe_name = if field_name == "type" { format!("r#{}", field_name) } else { field_name.to_string() };
let tag = field_proto.number().unwrap();
let f_type = field_proto.field_type().unwrap() as i32;
let (rust_type, method) = map_type_to_rust_builder(f_type);
output.push_str(&format!(
" pub fn {}(mut self, value: {}) -> Result<Self> {{\n self.builder.{}({}, value)?;\n Ok(self)\n }}\n\n",
safe_name, rust_type, method, tag
));
} }
output.push_str(&format!( if !generate_mod_files {
" pub fn finish(self) -> Result<&'b mut [u8]> {{\n self.builder.finish()\n }}\n}}\n\n" return generated_files;
)); }
let mut all_paths: Vec<String> = generated_files.iter().map(|(p, _)| p.clone()).collect();
all_paths.sort();
let mut mod_files: HashMap<String, HashSet<String>> = HashMap::new();
for path in &all_paths {
let parts: Vec<&str> = path.split('/').collect();
let mut current_dir = String::new();
for i in 0..parts.len() - 1 {
if !current_dir.is_empty() {
current_dir.push('/');
}
current_dir.push_str(parts[i]);
let mod_path = format!("{}/mod.rs", current_dir);
let sub_mod = parts[i + 1].replace(".rs", "");
mod_files.entry(mod_path).or_default().insert(sub_mod);
} }
} }
output let mut root_mods = HashSet::new();
for path in &all_paths {
let parts: Vec<&str> = path.split('/').collect();
root_mods.insert(parts[0].replace(".rs", ""));
}
let mut root_mod_content = String::new();
let mut sorted_root_mods: Vec<_> = root_mods.into_iter().collect();
sorted_root_mods.sort();
for m in sorted_root_mods {
root_mod_content.push_str(&format!("pub mod {};\n", m));
}
generated_files.push(("mod.rs".to_string(), root_mod_content));
for (mod_path, sub_mods) in mod_files {
let mut content = String::new();
let mut sorted_subs: Vec<_> = sub_mods.into_iter().collect();
sorted_subs.sort();
for sub in sorted_subs {
content.push_str(&format!("pub mod {};\n", sub));
}
generated_files.push((mod_path, content));
}
generated_files
} }
+1
View File
@@ -0,0 +1 @@
pub mod protobuf;
+1
View File
@@ -0,0 +1 @@
pub mod plugin;
+455
View File
@@ -0,0 +1,455 @@
use std::str;
// use crate::google::protobuf::descriptor;
pub struct Version<'a> {
accessor: crate::ProtoAccessor<'a>,
major_offset: Option<usize>,
minor_offset: Option<usize>,
patch_offset: Option<usize>,
suffix_offset: Option<usize>,
}
impl<'a> Version<'a> {
pub fn new(data: &'a [u8]) -> crate::Result<Self> {
let accessor = crate::ProtoAccessor::new(data)?;
let mut major_offset = None;
let mut minor_offset = None;
let mut patch_offset = None;
let mut suffix_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { major_offset = Some(offset); }
if tag.field_number == 2 { minor_offset = Some(offset); }
if tag.field_number == 3 { patch_offset = Some(offset); }
if tag.field_number == 4 { suffix_offset = Some(offset); }
}
Ok(Self {
accessor,
major_offset,
minor_offset,
patch_offset,
suffix_offset,
})
}
pub fn major(&self) -> crate::Result<i32> {
let offset = self.major_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation)
}
pub fn minor(&self) -> crate::Result<i32> {
let offset = self.minor_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation)
}
pub fn patch(&self) -> crate::Result<i32> {
let offset = self.patch_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation)
}
pub fn suffix(&self) -> crate::Result<&'a str> {
let offset = self.suffix_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation)
}
}
pub struct VersionBuilder<'b> {
builder: crate::ProtoBuilder<'b>,
}
impl<'b> VersionBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> VersionBuilder<'_> {
VersionBuilder {
builder: crate::ProtoBuilder::new(buf),
}
}
pub fn major(mut self, value: i32) -> crate::Result<Self> {
self.builder.write_int32(1, value)?;
Ok(self)
}
pub fn minor(mut self, value: i32) -> crate::Result<Self> {
self.builder.write_int32(2, value)?;
Ok(self)
}
pub fn patch(mut self, value: i32) -> crate::Result<Self> {
self.builder.write_int32(3, value)?;
Ok(self)
}
pub fn suffix(mut self, value: &str) -> crate::Result<Self> {
self.builder.write_string(4, value)?;
Ok(self)
}
pub fn finish(self) -> crate::Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct CodeGeneratorRequest<'a> {
accessor: crate::ProtoAccessor<'a>,
file_to_generate_start: Option<usize>,
file_to_generate_end: Option<usize>,
parameter_offset: Option<usize>,
proto_file_start: Option<usize>,
proto_file_end: Option<usize>,
source_file_descriptors_start: Option<usize>,
source_file_descriptors_end: Option<usize>,
compiler_version_offset: Option<usize>,
}
impl<'a> CodeGeneratorRequest<'a> {
pub fn new(data: &'a [u8]) -> crate::Result<Self> {
let accessor = crate::ProtoAccessor::new(data)?;
let mut file_to_generate_start = None;
let mut file_to_generate_end = None;
let mut parameter_offset = None;
let mut proto_file_start = None;
let mut proto_file_end = None;
let mut source_file_descriptors_start = None;
let mut source_file_descriptors_end = None;
let mut compiler_version_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 {
if file_to_generate_start.is_none() { file_to_generate_start = Some(offset); }
file_to_generate_end = Some(offset);
}
if tag.field_number == 2 { parameter_offset = Some(offset); }
if tag.field_number == 15 {
if proto_file_start.is_none() { proto_file_start = Some(offset); }
proto_file_end = Some(offset);
}
if tag.field_number == 17 {
if source_file_descriptors_start.is_none() { source_file_descriptors_start = Some(offset); }
source_file_descriptors_end = Some(offset);
}
if tag.field_number == 3 { compiler_version_offset = Some(offset); }
}
Ok(Self {
accessor,
file_to_generate_start, file_to_generate_end,
parameter_offset,
proto_file_start, proto_file_end,
source_file_descriptors_start, source_file_descriptors_end,
compiler_version_offset,
})
}
pub fn file_to_generate(&self) -> crate::RepeatedFieldIterator<'a> {
match (self.file_to_generate_start, self.file_to_generate_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(1, start, end),
_ => self.accessor.iter_repeated(1),
}
}
pub fn parameter(&self) -> crate::Result<&'a str> {
let offset = self.parameter_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation)
}
pub fn proto_file(&self) -> crate::RepeatedFieldIterator<'a> {
match (self.proto_file_start, self.proto_file_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end),
_ => self.accessor.iter_repeated(15),
}
}
pub fn source_file_descriptors(&self) -> crate::RepeatedFieldIterator<'a> {
match (self.source_file_descriptors_start, self.source_file_descriptors_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(17, start, end),
_ => self.accessor.iter_repeated(17),
}
}
pub fn compiler_version(&self) -> crate::Result<&'a [u8]> {
let offset = self.compiler_version_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes)
}
}
pub struct CodeGeneratorRequestBuilder<'b> {
builder: crate::ProtoBuilder<'b>,
}
impl<'b> CodeGeneratorRequestBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> CodeGeneratorRequestBuilder<'_> {
CodeGeneratorRequestBuilder {
builder: crate::ProtoBuilder::new(buf),
}
}
pub fn file_to_generate(mut self, value: &str) -> crate::Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn parameter(mut self, value: &str) -> crate::Result<Self> {
self.builder.write_string(2, value)?;
Ok(self)
}
pub fn proto_file(mut self, value: &[u8]) -> crate::Result<Self> {
self.builder.write_bytes(15, value)?;
Ok(self)
}
pub fn source_file_descriptors(mut self, value: &[u8]) -> crate::Result<Self> {
self.builder.write_bytes(17, value)?;
Ok(self)
}
pub fn compiler_version(mut self, value: &[u8]) -> crate::Result<Self> {
self.builder.write_bytes(3, value)?;
Ok(self)
}
pub fn finish(self) -> crate::Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct CodeGeneratorResponse<'a> {
accessor: crate::ProtoAccessor<'a>,
error_offset: Option<usize>,
supported_features_offset: Option<usize>,
minimum_edition_offset: Option<usize>,
maximum_edition_offset: Option<usize>,
file_start: Option<usize>,
file_end: Option<usize>,
}
impl<'a> CodeGeneratorResponse<'a> {
pub fn new(data: &'a [u8]) -> crate::Result<Self> {
let accessor = crate::ProtoAccessor::new(data)?;
let mut error_offset = None;
let mut supported_features_offset = None;
let mut minimum_edition_offset = None;
let mut maximum_edition_offset = None;
let mut file_start = None;
let mut file_end = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { error_offset = Some(offset); }
if tag.field_number == 2 { supported_features_offset = Some(offset); }
if tag.field_number == 3 { minimum_edition_offset = Some(offset); }
if tag.field_number == 4 { maximum_edition_offset = Some(offset); }
if tag.field_number == 15 {
if file_start.is_none() { file_start = Some(offset); }
file_end = Some(offset);
}
}
Ok(Self {
accessor,
error_offset,
supported_features_offset,
minimum_edition_offset,
maximum_edition_offset,
file_start, file_end,
})
}
pub fn error(&self) -> crate::Result<&'a str> {
let offset = self.error_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation)
}
pub fn supported_features(&self) -> crate::Result<u32> {
let offset = self.supported_features_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as u32).map_err(|_| crate::RotoError::WireFormatViolation)
}
pub fn minimum_edition(&self) -> crate::Result<i32> {
let offset = self.minimum_edition_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation)
}
pub fn maximum_edition(&self) -> crate::Result<i32> {
let offset = self.maximum_edition_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation)
}
pub fn file(&self) -> crate::RepeatedFieldIterator<'a> {
match (self.file_start, self.file_end) {
(Some(start), Some(end)) => self.accessor.iter_repeated_range(15, start, end),
_ => self.accessor.iter_repeated(15),
}
}
}
pub struct CodeGeneratorResponseBuilder<'b> {
builder: crate::ProtoBuilder<'b>,
}
impl<'b> CodeGeneratorResponseBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> CodeGeneratorResponseBuilder<'_> {
CodeGeneratorResponseBuilder {
builder: crate::ProtoBuilder::new(buf),
}
}
pub fn error(mut self, value: &str) -> crate::Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn supported_features(mut self, value: u64) -> crate::Result<Self> {
self.builder.write_varint(2, value)?;
Ok(self)
}
pub fn minimum_edition(mut self, value: i32) -> crate::Result<Self> {
self.builder.write_int32(3, value)?;
Ok(self)
}
pub fn maximum_edition(mut self, value: i32) -> crate::Result<Self> {
self.builder.write_int32(4, value)?;
Ok(self)
}
pub fn file(mut self, value: &[u8]) -> crate::Result<Self> {
self.builder.write_bytes(15, value)?;
Ok(self)
}
pub fn finish(self) -> crate::Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub mod code_generator_response {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum Feature {
FEATURENONE = 0,
FEATUREPROTO3OPTIONAL = 1,
FEATURESUPPORTSEDITIONS = 2,
}
impl Feature {
pub fn from_i32(value: i32) -> Self {
match value {
0 => Feature::FEATURENONE,
1 => Feature::FEATUREPROTO3OPTIONAL,
2 => Feature::FEATURESUPPORTSEDITIONS,
_ => Feature::FEATURENONE,
}
}
}
pub struct File<'a> {
accessor: crate::ProtoAccessor<'a>,
name_offset: Option<usize>,
insertion_point_offset: Option<usize>,
content_offset: Option<usize>,
generated_code_info_offset: Option<usize>,
}
impl<'a> File<'a> {
pub fn new(data: &'a [u8]) -> crate::Result<Self> {
let accessor = crate::ProtoAccessor::new(data)?;
let mut name_offset = None;
let mut insertion_point_offset = None;
let mut content_offset = None;
let mut generated_code_info_offset = None;
for item in accessor.fields() {
let (offset, tag, _) = item?;
if tag.field_number == 1 { name_offset = Some(offset); }
if tag.field_number == 2 { insertion_point_offset = Some(offset); }
if tag.field_number == 15 { content_offset = Some(offset); }
if tag.field_number == 16 { generated_code_info_offset = Some(offset); }
}
Ok(Self {
accessor,
name_offset,
insertion_point_offset,
content_offset,
generated_code_info_offset,
})
}
pub fn name(&self) -> crate::Result<&'a str> {
let offset = self.name_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation)
}
pub fn insertion_point(&self) -> crate::Result<&'a str> {
let offset = self.insertion_point_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation)
}
pub fn content(&self) -> crate::Result<&'a str> {
let offset = self.content_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation)
}
pub fn generated_code_info(&self) -> crate::Result<&'a [u8]> {
let offset = self.generated_code_info_offset.ok_or(crate::RotoError::FieldNotFound)?;
let (bytes, _) = self.accessor.get_value_at(offset)?;
Ok(bytes)
}
}
pub struct FileBuilder<'b> {
builder: crate::ProtoBuilder<'b>,
}
impl<'b> FileBuilder<'b> {
pub fn builder(buf: &mut [u8]) -> FileBuilder<'_> {
FileBuilder {
builder: crate::ProtoBuilder::new(buf),
}
}
pub fn name(mut self, value: &str) -> crate::Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn insertion_point(mut self, value: &str) -> crate::Result<Self> {
self.builder.write_string(2, value)?;
Ok(self)
}
pub fn content(mut self, value: &str) -> crate::Result<Self> {
self.builder.write_string(15, value)?;
Ok(self)
}
pub fn generated_code_info(mut self, value: &[u8]) -> crate::Result<Self> {
self.builder.write_bytes(16, value)?;
Ok(self)
}
pub fn finish(self) -> crate::Result<&'b mut [u8]> {
self.builder.finish()
}
}
}
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
pub mod compiler;
pub mod descriptor;
+51 -7
View File
@@ -1,5 +1,5 @@
pub mod proto_gen;
pub mod generator; pub mod generator;
pub mod google;
// Uncomment this to check if the code compiles // Uncomment this to check if the code compiles
// #[path = "../proto/google/protobuf/descriptor.rs"] // #[path = "../proto/google/protobuf/descriptor.rs"]
// pub mod descriptor; // pub mod descriptor;
@@ -189,7 +189,7 @@ impl<'a> ProtoAccessor<'a> {
pub fn get_value(&self, field_number: u32) -> Result<(&'a [u8], WireType)> { pub fn get_value(&self, field_number: u32) -> Result<(&'a [u8], WireType)> {
let mut last_value = None; let mut last_value = None;
for item in self.fields() { for item in self.fields() {
let (tag, value) = item?; let (_offset, tag, value) = item?;
if tag.field_number == field_number { if tag.field_number == field_number {
last_value = Some((value, tag.wire_type)); last_value = Some((value, tag.wire_type));
} }
@@ -201,6 +201,32 @@ impl<'a> ProtoAccessor<'a> {
pub fn iter_repeated(&self, field_number: u32) -> RepeatedFieldIterator<'a> { pub fn iter_repeated(&self, field_number: u32) -> RepeatedFieldIterator<'a> {
RepeatedFieldIterator::new(self.data, field_number) RepeatedFieldIterator::new(self.data, field_number)
} }
/// Returns the value and wire type of a field at a specific offset.
pub fn get_value_at(&self, offset: usize) -> Result<(&'a [u8], WireType)> {
if offset >= self.data.len() {
return Err(RotoError::UnexpectedEndOfBuffer);
}
let (tag, tag_len) = Tag::decode(&self.data[offset..])?;
let cursor_after_tag = offset + tag_len;
if cursor_after_tag > self.data.len() {
return Err(RotoError::UnexpectedEndOfBuffer);
}
let value_len = skip_value(tag.wire_type, &self.data[cursor_after_tag..])?;
let (value_offset, actual_value_len) = match tag.wire_type {
WireType::LengthDelimited => {
let (_, varint_len) = read_varint(&self.data[cursor_after_tag..])?;
(cursor_after_tag + varint_len, value_len - varint_len)
}
_ => (cursor_after_tag, value_len),
};
Ok((&self.data[value_offset..value_offset + actual_value_len], tag.wire_type))
}
/// Returns an iterator that scans a specific range of the buffer for all occurrences of the specified field.
pub fn iter_repeated_range(&self, field_number: u32, start: usize, end: usize) -> RepeatedFieldIterator<'a> {
RepeatedFieldIterator::new_range(self.data, field_number, start, end)
}
} }
pub struct FieldIterator<'a> { pub struct FieldIterator<'a> {
@@ -209,7 +235,7 @@ pub struct FieldIterator<'a> {
} }
impl<'a> Iterator for FieldIterator<'a> { impl<'a> Iterator for FieldIterator<'a> {
type Item = Result<(Tag, &'a [u8])>; type Item = Result<(usize, Tag, &'a [u8])>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.cursor >= self.data.len() { if self.cursor >= self.data.len() {
@@ -254,23 +280,36 @@ impl<'a> Iterator for FieldIterator<'a> {
self.cursor = cursor_after_tag + value_len; self.cursor = cursor_after_tag + value_len;
Some(Ok((tag, &self.data[value_offset..value_offset + actual_value_len]))) Some(Ok((self.cursor - tag_len - value_len, tag, &self.data[value_offset..value_offset + actual_value_len])))
} }
} }
pub struct RepeatedFieldIterator<'a> { pub struct RepeatedFieldIterator<'a> {
iterator: FieldIterator<'a>, iterator: FieldIterator<'a>,
field_number: u32, field_number: u32,
end_offset: Option<usize>,
} }
impl<'a> RepeatedFieldIterator<'a> { impl<'a> RepeatedFieldIterator<'a> {
fn new(data: &'a [u8], field_number: u32) -> Self { pub fn new(data: &'a [u8], field_number: u32) -> Self {
Self { Self {
iterator: FieldIterator { iterator: FieldIterator {
data, data,
cursor: 0, cursor: 0,
}, },
field_number, field_number,
end_offset: None,
}
}
pub fn new_range(data: &'a [u8], field_number: u32, start: usize, end: usize) -> Self {
Self {
iterator: FieldIterator {
data,
cursor: start,
},
field_number,
end_offset: Some(end),
} }
} }
} }
@@ -281,7 +320,12 @@ impl<'a> Iterator for RepeatedFieldIterator<'a> {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
while let Some(item) = self.iterator.next() { while let Some(item) = self.iterator.next() {
match item { match item {
Ok((tag, value)) if tag.field_number == self.field_number => { Ok((offset, tag, value)) if tag.field_number == self.field_number => {
if let Some(end) = self.end_offset {
if offset > end {
return None;
}
}
return Some(Ok((value, tag.wire_type))); return Some(Ok((value, tag.wire_type)));
} }
Ok(_) => continue, Ok(_) => continue,
@@ -476,7 +520,7 @@ mod tests {
// Validate that fields appear in the expected relative order // Validate that fields appear in the expected relative order
let field_numbers: Vec<u32> = acc.fields() let field_numbers: Vec<u32> = acc.fields()
.map(|r| r.expect("Failed to decode field").0.field_number) .map(|r| r.expect("Failed to decode field").1.field_number)
.collect(); .collect();
let essential_fields = [1, 2, 3, 14, 16, 20]; let essential_fields = [1, 2, 3, 14, 16, 20];
@@ -1,281 +0,0 @@
use crate::{ProtoAccessor, ProtoBuilder, Result, RotoError};
use crate::proto_gen::google::protobuf::descriptor::{FileDescriptorProto, GeneratedCodeInfo};
use std::str;
pub struct Version<'a>(ProtoAccessor<'a>);
impl<'a> Version<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn major(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(1)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn minor(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(2)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn patch(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(3)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn suffix(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(4)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn builder(buf: &mut [u8]) -> VersionBuilder<'_> {
VersionBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct VersionBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> VersionBuilder<'b> {
pub fn major(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(1, value)?;
Ok(self)
}
pub fn minor(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(2, value)?;
Ok(self)
}
pub fn patch(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(3, value)?;
Ok(self)
}
pub fn suffix(mut self, value: &str) -> Result<Self> {
self.builder.write_string(4, value)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct CodeGeneratorRequest<'a>(ProtoAccessor<'a>);
impl<'a> CodeGeneratorRequest<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn file_to_generate(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(1)
}
pub fn parameter(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(2)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn compiler_version(&self) -> Result<Version<'a>> {
let (bytes, _) = self.0.get_value(3)?;
Version::new(bytes)
}
pub fn proto_file(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(15)
}
pub fn source_file_descriptors(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(17)
}
pub fn builder(buf: &mut [u8]) -> CodeGeneratorRequestBuilder<'_> {
CodeGeneratorRequestBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct CodeGeneratorRequestBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> CodeGeneratorRequestBuilder<'b> {
pub fn add_file_to_generate(mut self, value: &str) -> Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn parameter(mut self, value: &str) -> Result<Self> {
self.builder.write_string(2, value)?;
Ok(self)
}
pub fn compiler_version(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(3, data)?;
Ok(self)
}
pub fn add_proto_file(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(15, data)?;
Ok(self)
}
pub fn add_source_file_descriptor(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(17, data)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct CodeGeneratorResponse<'a>(ProtoAccessor<'a>);
impl<'a> CodeGeneratorResponse<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn error(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(1)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn supported_features(&self) -> Result<u64> {
let (bytes, _) = self.0.get_value(2)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val)
}
pub fn minimum_edition(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(3)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn maximum_edition(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(4)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn file(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(15)
}
pub fn builder(buf: &mut [u8]) -> CodeGeneratorResponseBuilder<'_> {
CodeGeneratorResponseBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct CodeGeneratorResponseBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> CodeGeneratorResponseBuilder<'b> {
pub fn error(mut self, value: &str) -> Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn supported_features(mut self, value: u64) -> Result<Self> {
self.builder.write_varint(2, value)?;
Ok(self)
}
pub fn minimum_edition(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(3, value)?;
Ok(self)
}
pub fn maximum_edition(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(4, value)?;
Ok(self)
}
pub fn add_file(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(15, data)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct CodeGeneratorResponseFile<'a>(ProtoAccessor<'a>);
impl<'a> CodeGeneratorResponseFile<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn name(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(1)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn insertion_point(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(2)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn content(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(15)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn generated_code_info(&self) -> Result<GeneratedCodeInfo<'a>> {
let (bytes, _) = self.0.get_value(16)?;
GeneratedCodeInfo::new(bytes)
}
pub fn builder(buf: &mut [u8]) -> CodeGeneratorResponseFileBuilder<'_> {
CodeGeneratorResponseFileBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct CodeGeneratorResponseFileBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> CodeGeneratorResponseFileBuilder<'b> {
pub fn name(mut self, value: &str) -> Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn insertion_point(mut self, value: &str) -> Result<Self> {
self.builder.write_string(2, value)?;
Ok(self)
}
pub fn content(mut self, value: &str) -> Result<Self> {
self.builder.write_string(15, value)?;
Ok(self)
}
pub fn generated_code_info(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(16, data)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
-905
View File
@@ -1,905 +0,0 @@
use crate::{ProtoAccessor, ProtoBuilder, Result, RotoError};
use std::str;
/// The edition of the proto file.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum Edition {
EditionLegacy = 0,
EditionProto2 = 1,
EditionProto3 = 2,
Edition2023 = 3,
Edition2024 = 4,
Edition2026 = 5,
}
impl Edition {
pub fn from_i32(value: i32) -> Self {
match value {
1 => Edition::EditionProto2,
2 => Edition::EditionProto3,
3 => Edition::Edition2023,
4 => Edition::Edition2024,
5 => Edition::Edition2026,
_ => Edition::EditionLegacy,
}
}
}
pub struct FileDescriptorSet<'a>(ProtoAccessor<'a>);
impl<'a> FileDescriptorSet<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn file(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(1)
}
}
pub struct FileDescriptorProto<'a>(ProtoAccessor<'a>);
impl<'a> FileDescriptorProto<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn name(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(1)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn package(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(2)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn dependency(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(3)
}
pub fn public_dependency(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(10)
}
pub fn weak_dependency(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(11)
}
pub fn option_dependency(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(15)
}
pub fn message_type(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(4)
}
pub fn enum_type(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(5)
}
pub fn service(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(6)
}
pub fn extension(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(7)
}
pub fn syntax(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(12)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn edition(&self) -> Result<Edition> {
let (bytes, _) = self.0.get_value(14)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(Edition::from_i32(val as i32))
}
pub fn builder(buf: &mut [u8]) -> FileDescriptorProtoBuilder<'_> {
FileDescriptorProtoBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct FileDescriptorProtoBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> FileDescriptorProtoBuilder<'b> {
pub fn name(mut self, value: &str) -> Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn package(mut self, value: &str) -> Result<Self> {
self.builder.write_string(2, value)?;
Ok(self)
}
pub fn add_dependency(mut self, value: &str) -> Result<Self> {
self.builder.write_string(3, value)?;
Ok(self)
}
pub fn add_public_dependency(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(10, value)?;
Ok(self)
}
pub fn add_weak_dependency(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(11, value)?;
Ok(self)
}
pub fn add_option_dependency(mut self, value: &str) -> Result<Self> {
self.builder.write_string(15, value)?;
Ok(self)
}
pub fn add_message_type(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(4, data)?;
Ok(self)
}
pub fn add_enum_type(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(5, data)?;
Ok(self)
}
pub fn add_service(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(6, data)?;
Ok(self)
}
pub fn add_extension(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(7, data)?;
Ok(self)
}
pub fn syntax(mut self, value: &str) -> Result<Self> {
self.builder.write_string(12, value)?;
Ok(self)
}
pub fn edition(mut self, value: Edition) -> Result<Self> {
self.builder.write_varint(14, value as u64)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct DescriptorProto<'a>(ProtoAccessor<'a>);
impl<'a> DescriptorProto<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn name(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(1)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn field(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(2)
}
pub fn extension(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(6)
}
pub fn nested_type(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(3)
}
pub fn enum_type(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(4)
}
pub fn reserved_name(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(10)
}
pub fn builder(buf: &mut [u8]) -> DescriptorProtoBuilder<'_> {
DescriptorProtoBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct DescriptorProtoBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> DescriptorProtoBuilder<'b> {
pub fn name(mut self, value: &str) -> Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn add_field(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(2, data)?;
Ok(self)
}
pub fn add_extension(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(6, data)?;
Ok(self)
}
pub fn add_nested_type(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(3, data)?;
Ok(self)
}
pub fn add_enum_type(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(4, data)?;
Ok(self)
}
pub fn add_reserved_name(mut self, value: &str) -> Result<Self> {
self.builder.write_string(10, value)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct EnumDescriptorProto<'a>(ProtoAccessor<'a>);
impl<'a> EnumDescriptorProto<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn name(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(1)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn enum_value(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(2)
}
pub fn reserved_value(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(3)
}
pub fn options(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(4)
}
pub fn builder(buf: &mut [u8]) -> EnumDescriptorProtoBuilder<'_> {
EnumDescriptorProtoBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct EnumDescriptorProtoBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> EnumDescriptorProtoBuilder<'b> {
pub fn name(mut self, value: &str) -> Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn add_enum_value(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(2, data)?;
Ok(self)
}
pub fn add_reserved_value(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(3, value)?;
Ok(self)
}
pub fn add_options(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(4, data)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FieldType {
Double = 1,
Float = 2,
Int64 = 3,
Uint64 = 4,
Int32 = 5,
Fixed64 = 6,
Fixed32 = 7,
Bool = 8,
String = 9,
Group = 10,
Message = 11,
Bytes = 12,
Uint32 = 13,
Enum = 14,
Sfixed32 = 15,
Sfixed64 = 16,
Sint32 = 17,
Sint64 = 18,
}
impl FieldType {
pub fn from_i32(value: i32) -> Self {
match value {
1 => FieldType::Double,
2 => FieldType::Float,
3 => FieldType::Int64,
4 => FieldType::Uint64,
5 => FieldType::Int32,
6 => FieldType::Fixed64,
7 => FieldType::Fixed32,
8 => FieldType::Bool,
9 => FieldType::String,
10 => FieldType::Group,
11 => FieldType::Message,
12 => FieldType::Bytes,
13 => FieldType::Uint32,
14 => FieldType::Enum,
15 => FieldType::Sfixed32,
16 => FieldType::Sfixed64,
17 => FieldType::Sint32,
18 => FieldType::Sint64,
_ => FieldType::Int32,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FieldLabel {
Optional = 1,
Required = 2,
Repeated = 3,
}
impl FieldLabel {
pub fn from_i32(value: i32) -> Self {
match value {
1 => FieldLabel::Optional,
2 => FieldLabel::Required,
3 => FieldLabel::Repeated,
_ => FieldLabel::Optional,
}
}
}
pub struct FieldDescriptorProto<'a>(ProtoAccessor<'a>);
impl<'a> FieldDescriptorProto<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn name(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(1)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn number(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(3)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn label(&self) -> Result<FieldLabel> {
let (bytes, _) = self.0.get_value(4)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(FieldLabel::from_i32(val as i32))
}
pub fn field_type(&self) -> Result<FieldType> {
let (bytes, _) = self.0.get_value(5)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(FieldType::from_i32(val as i32))
}
pub fn type_name(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(6)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn default_value(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(7)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn oneof_index(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(9)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn json_name(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(10)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn proto3_optional(&self) -> Result<bool> {
let (bytes, _) = self.0.get_value(17)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val != 0)
}
pub fn builder(buf: &mut [u8]) -> FieldDescriptorProtoBuilder<'_> {
FieldDescriptorProtoBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct FieldDescriptorProtoBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> FieldDescriptorProtoBuilder<'b> {
pub fn name(mut self, value: &str) -> Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn number(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(3, value)?;
Ok(self)
}
pub fn label(mut self, value: FieldLabel) -> Result<Self> {
self.builder.write_varint(4, value as u64)?;
Ok(self)
}
pub fn field_type(mut self, value: FieldType) -> Result<Self> {
self.builder.write_varint(5, value as u64)?;
Ok(self)
}
pub fn type_name(mut self, value: &str) -> Result<Self> {
self.builder.write_string(6, value)?;
Ok(self)
}
pub fn default_value(mut self, value: &str) -> Result<Self> {
self.builder.write_string(7, value)?;
Ok(self)
}
pub fn oneof_index(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(9, value)?;
Ok(self)
}
pub fn json_name(mut self, value: &str) -> Result<Self> {
self.builder.write_string(10, value)?;
Ok(self)
}
pub fn proto3_optional(mut self, value: bool) -> Result<Self> {
self.builder.write_varint(17, if value { 1 } else { 0 })?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct GeneratedCodeInfo<'a>(ProtoAccessor<'a>);
impl<'a> GeneratedCodeInfo<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn annotation(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(1)
}
pub fn builder(buf: &mut [u8]) -> GeneratedCodeInfoBuilder<'_> {
GeneratedCodeInfoBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct GeneratedCodeInfoBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> GeneratedCodeInfoBuilder<'b> {
pub fn add_annotation(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(1, data)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct Version<'a>(ProtoAccessor<'a>);
impl<'a> Version<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn major(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(1)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn minor(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(2)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn patch(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(3)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn suffix(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(4)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn builder(buf: &mut [u8]) -> VersionBuilder<'_> {
VersionBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct VersionBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> VersionBuilder<'b> {
pub fn major(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(1, value)?;
Ok(self)
}
pub fn minor(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(2, value)?;
Ok(self)
}
pub fn patch(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(3, value)?;
Ok(self)
}
pub fn suffix(mut self, value: &str) -> Result<Self> {
self.builder.write_string(4, value)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct CodeGeneratorRequest<'a>(ProtoAccessor<'a>);
impl<'a> CodeGeneratorRequest<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn file_to_generate(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(1)
}
pub fn parameter(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(2)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn compiler_version(&self) -> Result<&'a [u8]> {
let (bytes, _) = self.0.get_value(3)?;
Ok(bytes)
}
pub fn proto_file(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(15)
}
pub fn source_file_descriptors(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(17)
}
pub fn builder(buf: &mut [u8]) -> CodeGeneratorRequestBuilder<'_> {
CodeGeneratorRequestBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct CodeGeneratorRequestBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> CodeGeneratorRequestBuilder<'b> {
pub fn add_file_to_generate(mut self, value: &str) -> Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn parameter(mut self, value: &str) -> Result<Self> {
self.builder.write_string(2, value)?;
Ok(self)
}
pub fn compiler_version(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(3, data)?;
Ok(self)
}
pub fn add_proto_file(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(15, data)?;
Ok(self)
}
pub fn add_source_file_descriptor(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(17, data)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct CodeGeneratorResponse<'a>(ProtoAccessor<'a>);
impl<'a> CodeGeneratorResponse<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn error(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(1)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn supported_features(&self) -> Result<u64> {
let (bytes, _) = self.0.get_value(2)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val)
}
pub fn minimum_edition(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(3)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn maximum_edition(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(4)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn file(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(15)
}
pub fn builder(buf: &mut [u8]) -> CodeGeneratorResponseBuilder<'_> {
CodeGeneratorResponseBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct CodeGeneratorResponseBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> CodeGeneratorResponseBuilder<'b> {
pub fn error(mut self, value: &str) -> Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn supported_features(mut self, value: u64) -> Result<Self> {
self.builder.write_varint(2, value)?;
Ok(self)
}
pub fn minimum_edition(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(3, value)?;
Ok(self)
}
pub fn maximum_edition(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(4, value)?;
Ok(self)
}
pub fn add_file(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(15, data)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
pub struct ResponseFile<'a>(ProtoAccessor<'a>);
impl<'a> ResponseFile<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn name(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(1)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn insertion_point(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(2)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn content(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(15)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn generated_code_info(&self) -> Result<&'a [u8]> {
let (bytes, _) = self.0.get_value(16)?;
Ok(bytes)
}
pub fn builder(buf: &mut [u8]) -> ResponseFileBuilder<'_> {
ResponseFileBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct ResponseFileBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> ResponseFileBuilder<'b> {
pub fn name(mut self, value: &str) -> Result<Self> {
self.builder.write_string(1, value)?;
Ok(self)
}
pub fn insertion_point(mut self, value: &str) -> Result<Self> {
self.builder.write_string(2, value)?;
Ok(self)
}
pub fn content(mut self, value: &str) -> Result<Self> {
self.builder.write_string(15, value)?;
Ok(self)
}
pub fn generated_code_info(mut self, data: &[u8]) -> Result<Self> {
self.builder.write_bytes(16, data)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn test_read_descriptor_set() {
let data = include_bytes!("../../../../data/test_types.desc");
let set = FileDescriptorSet::new(data).expect("Failed to parse FileDescriptorSet");
let mut files = set.file();
let (file_data, _) = files.next().expect("No file descriptors found").expect("Failed to read file descriptor");
let file_proto = FileDescriptorProto::new(file_data).expect("Failed to parse FileDescriptorProto");
assert_eq!(file_proto.name().unwrap(), "data/test_types.proto");
assert_eq!(file_proto.package().unwrap(), "roto.test");
}
}
pub struct Annotation<'a>(ProtoAccessor<'a>);
impl<'a> Annotation<'a> {
pub fn new(data: &'a [u8]) -> Result<Self> {
Ok(Self(ProtoAccessor::new(data)?))
}
pub fn path(&self) -> crate::RepeatedFieldIterator<'a> {
self.0.iter_repeated(1)
}
pub fn source_file(&self) -> Result<&'a str> {
let (bytes, _) = self.0.get_value(2)?;
str::from_utf8(bytes).map_err(|_| RotoError::WireFormatViolation)
}
pub fn begin(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(3)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn end(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(4)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn semantic(&self) -> Result<i32> {
let (bytes, _) = self.0.get_value(5)?;
let (val, _) = crate::read_varint(bytes)?;
Ok(val as i32)
}
pub fn builder(buf: &mut [u8]) -> AnnotationBuilder<'_> {
AnnotationBuilder {
builder: ProtoBuilder::new(buf),
}
}
}
pub struct AnnotationBuilder<'b> {
builder: ProtoBuilder<'b>,
}
impl<'b> AnnotationBuilder<'b> {
pub fn add_path(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(1, value)?;
Ok(self)
}
pub fn source_file(mut self, value: &str) -> Result<Self> {
self.builder.write_string(2, value)?;
Ok(self)
}
pub fn begin(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(3, value)?;
Ok(self)
}
pub fn end(mut self, value: i32) -> Result<Self> {
self.builder.write_int32(4, value)?;
Ok(self)
}
pub fn semantic(mut self, value: i32) -> Result<Self> {
self.builder.write_varint(5, value as u64)?;
Ok(self)
}
pub fn finish(self) -> Result<&'b mut [u8]> {
self.builder.finish()
}
}
File diff suppressed because it is too large Load Diff
-8
View File
@@ -1,8 +0,0 @@
pub mod google {
pub mod protobuf {
pub mod descriptor;
pub mod compiler {
pub mod plugin;
}
}
}
+16 -6
View File
@@ -1,13 +1,18 @@
use std::fs; use std::fs;
use std::process::Command; use std::process::Command;
use std::path::PathBuf; use roto::google::protobuf::descriptor::{
FileDescriptorSet
};
use roto::google::protobuf::compiler::plugin::{
CodeGeneratorRequest,
};
#[test] #[test]
fn test_generated_code_builds() { fn test_generated_code_builds() {
// 1. Generate Rust code from data/request.bin // 1. Generate Rust code from data/request.bin
let request_path = "data/request.bin"; let request_path = "data/request.bin";
let data = fs::read(request_path).expect("Failed to read request.bin"); let data = fs::read(request_path).expect("Failed to read request.bin");
let request = roto::proto_gen::google::protobuf::descriptor::CodeGeneratorRequest::new(&data) let request = CodeGeneratorRequest::new(&data)
.expect("Failed to parse CodeGeneratorRequest"); .expect("Failed to parse CodeGeneratorRequest");
// Mimic the logic from protoc-gen-roto to build a FileDescriptorSet // Mimic the logic from protoc-gen-roto to build a FileDescriptorSet
@@ -27,11 +32,11 @@ fn test_generated_code_builds() {
// Write data // Write data
set_buf.extend_from_slice(file_data); set_buf.extend_from_slice(file_data);
} }
let set = roto::proto_gen::google::protobuf::descriptor::FileDescriptorSet::new(&set_buf) let set = FileDescriptorSet::new(&set_buf)
.expect("Failed to create FileDescriptorSet"); .expect("Failed to create FileDescriptorSet");
let generated_code = roto::generator::generate_rust_code(&set); let generated_files = roto::generator::generate_rust_code(&set, None, false);
assert!(!generated_code.is_empty(), "Generated code should not be empty"); assert!(!generated_files.is_empty(), "Generated code should not be empty");
// 2. Setup a temporary Cargo project to verify the code builds // 2. Setup a temporary Cargo project to verify the code builds
let root = std::env::current_dir().expect("Failed to get current directory"); let root = std::env::current_dir().expect("Failed to get current directory");
@@ -62,7 +67,12 @@ fn test_generated_code_builds() {
// 4. Write the generated code to src/lib.rs // 4. Write the generated code to src/lib.rs
// The generated code uses `use crate::{...}`, but it's now in a separate crate. // The generated code uses `use crate::{...}`, but it's now in a separate crate.
// Replace `crate` with `roto` to reference the types in the dependency. // Replace `crate` with `roto` to reference the types in the dependency.
let final_code = generated_code.replace("use crate::", "use roto::"); let mut all_code = String::new();
for (_, content) in generated_files {
all_code.push_str(&content);
all_code.push_str("\n");
}
let final_code = all_code.replace("use crate::", "use roto::");
let lib_path = temp_project_dir.join("src/lib.rs"); let lib_path = temp_project_dir.join("src/lib.rs");
fs::write(lib_path, final_code).expect("Failed to write generated code to src/lib.rs"); fs::write(lib_path, final_code).expect("Failed to write generated code to src/lib.rs");
+46
View File
@@ -0,0 +1,46 @@
use roto::generator::generate_rust_code;
use roto::google::protobuf::descriptor::{
FileDescriptorSet
};
use std::fs;
#[test]
fn test_nested_proto_generation_contains_modules() {
let request_path = "data/request.bin";
if !std::path::Path::new(request_path).exists() {
panic!("data/request.bin not found. This test requires the sample request binary.");
}
let data = fs::read(request_path).expect("Failed to read request.bin");
// The existing test logic to build a FileDescriptorSet from CodeGeneratorRequest
// We can simplify this by just wrapping the data if it's already a FileDescriptorSet,
// but request.bin is usually a CodeGeneratorRequest.
// Let's use the same logic as build_generated_code.rs to get a FileDescriptorSet
let request = roto::google::protobuf::compiler::plugin::CodeGeneratorRequest::new(&data)
.expect("Failed to parse CodeGeneratorRequest");
let mut set_buf = Vec::new();
for file_res in request.proto_file() {
let (file_data, _) = file_res.expect("Failed to iterate proto_file");
set_buf.push(10);
let len = file_data.len() as u64;
let mut len_buf = [0u8; 10];
let len_size = roto::write_varint(len, &mut len_buf).expect("Failed to write varint length");
set_buf.extend_from_slice(&len_buf[..len_size]);
set_buf.extend_from_slice(file_data);
}
let set = FileDescriptorSet::new(&set_buf).expect("Failed to create FileDescriptorSet");
let generated_files = generate_rust_code(&set, None, false);
let all_code: String = generated_files.into_iter().map(|(_, content)| content).collect();
println!("Generated Code:\n{}", all_code);
// We want to see if any message has a nested module.
// Since we don't know exactly what's in request.bin, we'll look for ANY 'pub mod' inside the generated code
// that isn't at the top level (though the generator puts them inside the message definition).
assert!(all_code.contains("pub mod "), "Generated code should contain at least one nested module for nested types");
assert!(all_code.contains("pub struct "), "Generated code should contain structs");
}