RAPE-toolkit/descriptor-to-proto/src/lib.rs
2025-03-27 00:23:37 +01:00

272 lines
9.1 KiB
Rust

mod extend;
use crate::extend::AsStrType;
use prost::Message;
use prost_types::field_descriptor_proto::Label;
use prost_types::{
DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, FileDescriptorProto, FileOptions,
ServiceDescriptorProto, SourceCodeInfo,
};
use std::collections::HashMap;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ConverterError {
#[error("Proto decoding error")]
DecodeError(#[from] prost::DecodeError),
#[error("Internal extend error")]
ExtendError(#[from] extend::ExtendError),
#[error("{0}")]
GenericError(String),
}
enum Syntax {
Proto2,
Proto3,
}
pub fn process_file_descriptor_set(
file_descriptor_set_data: &[u8],
) -> Result<HashMap<String, String>, ConverterError> {
let mut descriptors = HashMap::new();
let file_descriptor_set = prost_types::FileDescriptorSet::decode(file_descriptor_set_data)?;
for descriptor_proto in &file_descriptor_set.file {
let (file_name, content) = process_file_descriptor(descriptor_proto)?;
descriptors.insert(file_name, content);
}
Ok(descriptors)
}
fn process_file_descriptor(
descriptor: &FileDescriptorProto,
) -> Result<(String, String), ConverterError> {
let mut buffer = String::with_capacity(1024 * 1024); // 1MB
let syntax = process_version(&mut buffer, descriptor.syntax.as_ref());
let name = descriptor.name().to_string();
process_package(&mut buffer, descriptor.package.as_ref());
if let Some(options) = &descriptor.options {
process_options(&mut buffer, options)?;
}
for dependency in &descriptor.dependency {
buffer.push_str(format!("import \"{dependency}\";\n").as_str());
}
buffer.push('\n');
// TODO: Hande public_dependency and weak_dependency
process_enum(&mut buffer, &descriptor.enum_type)?;
process_message(
&mut buffer,
&syntax,
&descriptor.message_type,
descriptor.package.as_ref(),
)?;
process_service(&mut buffer, &descriptor.service)?;
process_extension(&mut buffer, &descriptor.extension)?;
if let Some(source_code_info) = &descriptor.source_code_info {
process_source_code_info(&mut buffer, source_code_info)?;
}
Ok((name, buffer))
}
fn process_version(buffer: &mut String, syntax: Option<&String>) -> Syntax {
// According to google, if no edition or syntax is specified, one shall default to proto2
let syntax = syntax.cloned().unwrap_or("proto2".to_string());
buffer.push_str(format!("syntax = \"{syntax}\";\n\n").as_str());
match syntax.as_str() {
"proto3" => Syntax::Proto3,
_ => Syntax::Proto2,
}
}
fn process_package(buffer: &mut String, package: Option<&String>) {
if let Some(package) = package {
buffer.push_str(format!("package {package};\n\n").as_str());
}
}
fn process_message(
buffer: &mut String,
syntax: &Syntax,
messages: &[DescriptorProto],
package: Option<&String>,
) -> Result<(), ConverterError> {
for message in messages {
buffer.push_str(format!("message {} {{\n", message.name()).as_str());
for field in &message.field {
buffer.push('\t');
// TODO: use label to know if optional shall be used
match field.label() {
Label::Optional => buffer.push_str("optional "),
Label::Required => match syntax {
Syntax::Proto2 => buffer.push_str("required "),
Syntax::Proto3 => {}
},
Label::Repeated => buffer.push_str("repeated "),
}
buffer.push_str(field.r#type().as_str_type(field.type_name.as_ref())?);
buffer.push_str(format!(" {} = {}", field.name(), field.number()).as_str());
if let Some(extendee) = &field.extendee {
// TODO: Handle extendee
return Err(ConverterError::GenericError(format!(
"Descriptor extendee is not supported {extendee:?}"
)));
}
if let Some(default_value) = &field.default_value {
// TODO: Handle default_value
return Err(ConverterError::GenericError(format!(
"Descriptor default_value is not supported {default_value:?}"
)));
}
if let Some(oneof_index) = &field.oneof_index {
// TODO: Handle oneof_index
return Err(ConverterError::GenericError(format!(
"Descriptor oneof_index is not supported {oneof_index:?}"
)));
}
if let Some(json_name) = &field.json_name {
buffer.push_str(format!(" [json_name=\"{json_name}\"]").as_str());
}
buffer.push_str(";\n");
}
process_extension(buffer, &message.extension)?;
// TODO: We might require indentation without external tool
process_message(buffer, syntax, &message.nested_type, package)?;
// TODO: We might require indentation without external tool
process_enum(buffer, &message.enum_type)?;
if !message.extension_range.is_empty() {
// TODO: Handle extension_range
return Err(ConverterError::GenericError(format!(
"Descriptor extension_range is not supported {:?}",
message.extension_range
)));
}
if !message.oneof_decl.is_empty() {
// TODO: Handle oneof_decl
return Err(ConverterError::GenericError(format!(
"Descriptor oneof_decl is not supported {:?}",
message.oneof_decl
)));
}
if let Some(options) = &message.options {
// TODO: Handle options
return Err(ConverterError::GenericError(format!(
"Descriptor options is not supported {options:?}"
)));
}
if !message.reserved_range.is_empty() {
// TODO: Handle reserved_range
return Err(ConverterError::GenericError(format!(
"Descriptor reserved_range is not supported {:?}",
message.reserved_range
)));
}
if !message.reserved_name.is_empty() {
// TODO: Handle reserved_name
return Err(ConverterError::GenericError(format!(
"Descriptor reserved_name is not supported {:?}",
message.reserved_name
)));
}
buffer.push_str("}\n\n");
}
Ok(())
}
fn process_enum(buffer: &mut String, enums: &[EnumDescriptorProto]) -> Result<(), ConverterError> {
for enum_descriptor in enums {
buffer.push_str(format!("enum {} {{\n", enum_descriptor.name()).as_str());
for variant in &enum_descriptor.value {
buffer.push_str(format!("\t{} = {};\n", variant.name(), variant.number()).as_str());
if let Some(options) = &variant.options {
// TODO: Handle options
return Err(ConverterError::GenericError(format!(
"Descriptor options is not supported {options:?}"
)));
}
}
if let Some(options) = &enum_descriptor.options {
// TODO: Handle options
return Err(ConverterError::GenericError(format!(
"Descriptor options is not supported {options:?}"
)));
}
if !enum_descriptor.reserved_range.is_empty() {
// TODO: Handle reserved_range
return Err(ConverterError::GenericError(format!(
"Descriptor reserved_range is not supported {:?}",
enum_descriptor.reserved_range
)));
}
if !enum_descriptor.reserved_name.is_empty() {
// TODO: Handle reserved_name
return Err(ConverterError::GenericError(format!(
"Descriptor reserved_name is not supported {:?}",
enum_descriptor.reserved_name
)));
}
buffer.push_str("}\n\n");
}
Ok(())
}
fn process_service(
_buffer: &mut String,
services: &[ServiceDescriptorProto],
) -> Result<(), ConverterError> {
if !services.is_empty() {
// TODO: Handle service
return Err(ConverterError::GenericError(format!(
"Descriptor service is not supported {services:?}"
)));
}
Ok(())
}
fn process_options(_buffer: &mut String, options: &FileOptions) -> Result<(), ConverterError> {
// TODO: Handle options
Err(ConverterError::GenericError(format!(
"Descriptor options is not supported {options:?}"
)))
}
fn process_extension(
_buffer: &mut String,
extension: &[FieldDescriptorProto],
) -> Result<(), ConverterError> {
if !extension.is_empty() {
// TODO: Handle extension
return Err(ConverterError::GenericError(format!(
"Descriptor extension is not supported {extension:?}"
)));
}
Ok(())
}
fn process_source_code_info(
_buffer: &mut String,
source_code_info: &SourceCodeInfo,
) -> Result<(), ConverterError> {
// TODO: Handle source_code_info
Err(ConverterError::GenericError(format!(
"Descriptor source_code_info is not supported {source_code_info:?}"
)))
}