blob: fd06b4ced8cca34d83c2fc2f34568e6ac0d62a56 [file] [log] [blame]
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()
);
}
}