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, 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:?}" ))) }