use protobuf::descriptor::field_descriptor_proto::Type; use protobuf::reflect::RuntimeFieldType; use protobuf::rt::tag_size; use protobuf_parse::ProtobufAbsPath; use crate::gen::code_writer::CodeWriter; use crate::gen::field::type_ext::TypeExt; use crate::gen::file_and_mod::FileAndMod; use crate::gen::inside::protobuf_crate_path; use crate::gen::message::RustTypeMessage; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust_types_values::message_or_enum_to_rust_relative; use crate::gen::rust_types_values::PrimitiveTypeVariant; use crate::gen::rust_types_values::RustType; use crate::gen::rust_types_values::RustValueTyped; use crate::gen::scope::EnumValueWithContext; use crate::gen::scope::FieldWithContext; use crate::gen::scope::MessageOrEnumWithScope; use crate::gen::scope::MessageWithScope; use crate::gen::scope::RootScope; use crate::Customize; #[derive(Clone, Debug)] pub(crate) struct FieldElemEnum<'a> { /// Enum default value variant, either from proto or from enum definition default_value: EnumValueWithContext<'a>, } impl<'a> FieldElemEnum<'a> { fn rust_name_relative(&self, reference: &FileAndMod) -> RustIdentWithPath { message_or_enum_to_rust_relative(&self.default_value.en, reference) } pub(crate) fn enum_rust_type(&self, reference: &FileAndMod) -> RustType { RustType::Enum( self.rust_name_relative(reference), self.default_value.rust_name(), self.default_value.proto.proto().number(), ) } fn enum_or_unknown_rust_type(&self, reference: &FileAndMod) -> RustType { RustType::EnumOrUnknown( self.rust_name_relative(reference), self.default_value.rust_name(), self.default_value.proto.proto().number(), ) } pub(crate) fn default_value_rust_expr(&self, reference: &FileAndMod) -> RustIdentWithPath { self.rust_name_relative(reference) .to_path() .with_ident(self.default_value.rust_name()) } } #[derive(Clone, Debug)] pub(crate) struct FieldElemMessage<'a> { pub message: MessageWithScope<'a>, } impl<'a> FieldElemMessage<'a> { pub(crate) fn rust_name_relative(&self, reference: &FileAndMod) -> RustTypeMessage { RustTypeMessage(message_or_enum_to_rust_relative(&self.message, reference)) } fn rust_type(&self, reference: &FileAndMod) -> RustType { RustType::Message(self.rust_name_relative(reference)) } } #[derive(Clone, Debug)] pub(crate) enum FieldElem<'a> { Primitive(Type, PrimitiveTypeVariant), Message(FieldElemMessage<'a>), Enum(FieldElemEnum<'a>), Group, } pub(crate) enum HowToGetMessageSize { Compute, GetCached, } impl<'a> FieldElem<'a> { pub(crate) fn proto_type(&self) -> Type { match *self { FieldElem::Primitive(t, ..) => t, FieldElem::Group => Type::TYPE_GROUP, FieldElem::Message(..) => Type::TYPE_MESSAGE, FieldElem::Enum(..) => Type::TYPE_ENUM, } } pub(crate) fn is_copy(&self) -> bool { self.proto_type().is_copy() } pub(crate) fn rust_storage_elem_type(&self, reference: &FileAndMod) -> RustType { match *self { FieldElem::Primitive(t, PrimitiveTypeVariant::Default) => t.rust_type(), FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) => { RustType::Chars } FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) => { RustType::Bytes } FieldElem::Primitive(.., PrimitiveTypeVariant::TokioBytes) => unreachable!(), FieldElem::Group => RustType::Group, FieldElem::Message(ref m) => m.rust_type(reference), FieldElem::Enum(ref en) => en.enum_or_unknown_rust_type(reference), } } // Type of set_xxx function parameter type for singular fields pub(crate) fn rust_set_xxx_param_type(&self, reference: &FileAndMod) -> RustType { if let FieldElem::Enum(ref en) = *self { en.enum_rust_type(reference) } else { self.rust_storage_elem_type(reference) } } pub(crate) fn primitive_type_variant(&self) -> PrimitiveTypeVariant { match self { &FieldElem::Primitive(_, v) => v, _ => PrimitiveTypeVariant::Default, } } pub(crate) fn singular_field_size( &self, field_number: u32, var: &RustValueTyped, customize: &Customize, ) -> String { let tag_size = tag_size(field_number); match self.proto_type().encoded_size() { Some(data_size) => format!("{tag_size} + {data_size}"), None => match self.proto_type() { Type::TYPE_MESSAGE => panic!("not a single-liner"), // We are not inlining `bytes_size` here, // assuming the compiler is smart enough to do it for us. // https://rust.godbolt.org/z/GrKa5zxq6 Type::TYPE_BYTES => format!( "{}::rt::bytes_size({}, &{})", protobuf_crate_path(customize), field_number, var.value ), Type::TYPE_STRING => format!( "{}::rt::string_size({}, &{})", protobuf_crate_path(customize), field_number, var.value ), Type::TYPE_ENUM => { format!( "{}::rt::int32_size({}, {}.value())", protobuf_crate_path(customize), field_number, var.value, ) } _ => { let param_type = match &var.rust_type { RustType::Ref(t) => (**t).clone(), t => t.clone(), }; let f = match self.proto_type() { Type::TYPE_SINT32 => "sint32_size", Type::TYPE_SINT64 => "sint64_size", Type::TYPE_INT32 => "int32_size", Type::TYPE_INT64 => "int64_size", Type::TYPE_UINT32 => "uint32_size", Type::TYPE_UINT64 => "uint64_size", t => unreachable!("unexpected type: {:?}", t), }; format!( "{}::rt::{f}({}, {})", protobuf_crate_path(customize), field_number, var.into_type(param_type, customize).value ) } }, } } pub(crate) fn write_element_size( &self, field_number: u32, item_var: &RustValueTyped, how_to_get_message_size: HowToGetMessageSize, sum_var: &str, customize: &Customize, w: &mut CodeWriter, ) { let tag_size = tag_size(field_number); match self.proto_type() { Type::TYPE_MESSAGE => { match how_to_get_message_size { HowToGetMessageSize::Compute => { w.write_line(&format!("let len = {}.compute_size();", item_var.value)) } HowToGetMessageSize::GetCached => w.write_line(&format!( "let len = {}.cached_size() as u64;", item_var.value )), } w.write_line(&format!( "{sum_var} += {tag_size} + {}::rt::compute_raw_varint64_size(len) + len;", protobuf_crate_path(customize), )); } _ => { w.write_line(&format!( "{sum_var} += {};", self.singular_field_size(field_number, item_var, customize) )); } } } pub(crate) fn write_write_element( &self, field_number: u32, v: &RustValueTyped, file_and_mod: &FileAndMod, customize: &Customize, os: &str, w: &mut CodeWriter, ) { match self.proto_type() { Type::TYPE_MESSAGE => { let param_type = RustType::Ref(Box::new(self.rust_storage_elem_type(file_and_mod))); w.write_line(&format!( "{}::rt::write_message_field_with_cached_size({}, {}, {})?;", protobuf_crate_path(customize), field_number, v.into_type(param_type, customize).value, os )); } _ => { let param_type = self.proto_type().os_write_fn_param_type(); let os_write_fn_suffix = self.proto_type().protobuf_name(); w.write_line(&format!( "{}.write_{}({}, {})?;", os, os_write_fn_suffix, field_number, v.into_type(param_type, customize).value )); } } } pub(crate) fn read_one_liner(&self) -> String { format!( "{}?", self.proto_type().read("is", self.primitive_type_variant()) ) } } pub(crate) fn field_elem<'a>( field: &FieldWithContext, root_scope: &'a RootScope<'a>, customize: &Customize, ) -> FieldElem<'a> { if let RuntimeFieldType::Map(..) = field.field.runtime_field_type() { unreachable!(); } if field.field.proto().type_() == Type::TYPE_GROUP { FieldElem::Group } else if field.field.proto().has_type_name() { let message_or_enum = root_scope .find_message_or_enum(&ProtobufAbsPath::from(field.field.proto().type_name())); match (field.field.proto().type_(), message_or_enum) { (Type::TYPE_MESSAGE, MessageOrEnumWithScope::Message(message)) => { FieldElem::Message(FieldElemMessage { message: message.clone(), }) } (Type::TYPE_ENUM, MessageOrEnumWithScope::Enum(enum_with_scope)) => { let default_value = if field.field.proto().has_default_value() { enum_with_scope.value_by_name(field.field.proto().default_value()) } else { enum_with_scope.values()[0].clone() }; FieldElem::Enum(FieldElemEnum { default_value }) } _ => panic!("unknown named type: {:?}", field.field.proto().type_()), } } else if field.field.proto().has_type() { let tokio_for_bytes = customize.tokio_bytes.unwrap_or(false); let tokio_for_string = customize.tokio_bytes_for_string.unwrap_or(false); let elem = match field.field.proto().type_() { Type::TYPE_STRING if tokio_for_string => { FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) } Type::TYPE_BYTES if tokio_for_bytes => { FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) } t => FieldElem::Primitive(t, PrimitiveTypeVariant::Default), }; elem } else { panic!( "neither type_name, nor field_type specified for field: {}", field.field.name() ); } }