use std::fmt; use protobuf::descriptor::*; use protobuf::reflect::FileDescriptor; use protobuf::reflect::MessageDescriptor; use protobuf_parse::snake_case; use crate::customize::ctx::CustomizeElemCtx; use crate::customize::ctx::SpecialFieldPseudoDescriptor; use crate::customize::rustproto_proto::customize_from_rustproto_for_message; use crate::gen::code_writer::*; use crate::gen::descriptor::write_fn_descriptor; use crate::gen::enums::*; use crate::gen::field::FieldGen; use crate::gen::field::FieldKind; use crate::gen::file_and_mod::FileAndMod; use crate::gen::inside::protobuf_crate_path; use crate::gen::oneof::OneofGen; use crate::gen::oneof::OneofVariantGen; use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_message; use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_special_field; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust::rel_path::RustRelativePath; use crate::gen::rust::snippets::expr_vec_with_capacity_const; use crate::gen::rust::snippets::EXPR_NONE; use crate::gen::rust_types_values::*; use crate::gen::scope::MessageWithScope; use crate::gen::scope::RootScope; use crate::gen::scope::WithScope; use crate::Customize; /// Protobuf message Rust type name #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct RustTypeMessage(pub RustIdentWithPath); impl fmt::Display for RustTypeMessage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } impl> From for RustTypeMessage { fn from(s: S) -> Self { RustTypeMessage(s.into()) } } impl RustTypeMessage { /// Code which emits default instance. pub fn default_instance(&self, customize: &Customize) -> String { format!( "<{} as {}::Message>::default_instance()", self.0, protobuf_crate_path(customize) ) } } /// Message info for codegen pub(crate) struct MessageGen<'a> { file_descriptor: &'a FileDescriptor, message_descriptor: MessageDescriptor, pub message: &'a MessageWithScope<'a>, pub root_scope: &'a RootScope<'a>, pub fields: Vec>, pub lite_runtime: bool, customize: CustomizeElemCtx<'a>, path: &'a [i32], info: Option<&'a SourceCodeInfo>, } impl<'a> MessageGen<'a> { pub fn new( file_descriptor: &'a FileDescriptor, message: &'a MessageWithScope<'a>, root_scope: &'a RootScope<'a>, parent_customize: &CustomizeElemCtx<'a>, path: &'a [i32], info: Option<&'a SourceCodeInfo>, ) -> anyhow::Result> { let message_descriptor = file_descriptor .message_by_package_relative_name(&format!("{}", message.protobuf_name_to_package())) .unwrap(); let customize = parent_customize.child( &customize_from_rustproto_for_message(message.message.proto().options.get_or_default()), &message.message, ); static FIELD_NUMBER: protobuf::rt::Lazy = protobuf::rt::Lazy::new(); let field_number = *FIELD_NUMBER.get(|| { protobuf::reflect::MessageDescriptor::for_type::() .field_by_name("field") .expect("`field` must exist") .proto() .number() }); let fields: Vec<_> = message .fields() .into_iter() .enumerate() .map(|(id, field)| { let mut path = path.to_vec(); path.extend_from_slice(&[field_number, id as i32]); FieldGen::parse(field, root_scope, &customize, path, info) }) .collect::>>()?; let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| { message.file_descriptor().proto().options.optimize_for() == file_options::OptimizeMode::LITE_RUNTIME }); Ok(MessageGen { message_descriptor, file_descriptor, message, root_scope, fields, lite_runtime, customize, path, info, }) } fn rust_name(&self) -> RustIdent { self.message.rust_name() } fn mod_name(&self) -> RustRelativePath { self.message.scope.rust_path_to_file() } pub fn file_and_mod(&self) -> FileAndMod { self.message .scope .file_and_mod(self.customize.for_elem.clone()) } fn oneofs(&'a self) -> Vec> { self.message .oneofs() .into_iter() .map(|oneof| OneofGen::parse(self, oneof, &self.customize)) .collect() } fn required_fields(&'a self) -> Vec<&'a FieldGen> { self.fields .iter() .filter(|f| match f.kind { FieldKind::Singular(ref singular) => singular.flag.is_required(), _ => false, }) .collect() } fn message_fields(&'a self) -> Vec<&'a FieldGen> { self.fields .iter() .filter(|f| f.proto_type == field_descriptor_proto::Type::TYPE_MESSAGE) .collect() } fn fields_except_oneof(&'a self) -> Vec<&'a FieldGen> { self.fields .iter() .filter(|f| match f.kind { FieldKind::Oneof(..) => false, _ => true, }) .collect() } fn fields_except_group(&'a self) -> Vec<&'a FieldGen> { self.fields .iter() .filter(|f| f.proto_type != field_descriptor_proto::Type::TYPE_GROUP) .collect() } fn fields_except_oneof_and_group(&'a self) -> Vec<&'a FieldGen> { self.fields .iter() .filter(|f| match f.kind { FieldKind::Oneof(..) => false, _ => f.proto_type != field_descriptor_proto::Type::TYPE_GROUP, }) .collect() } fn write_match_each_oneof_variant(&self, w: &mut CodeWriter, cb: F) where F: Fn(&mut CodeWriter, &OneofVariantGen, &RustValueTyped), { for oneof in self.oneofs() { let variants = oneof.variants_except_group(); if variants.is_empty() { // Special case because // https://github.com/rust-lang/rust/issues/50642 continue; } w.if_let_stmt( "::std::option::Option::Some(ref v)", &format!("self.{}", oneof.oneof.field_name())[..], |w| { w.match_block("v", |w| { for variant in variants { let ref field = variant.field; let (refv, vtype) = if field.elem_type_is_copy() { ("v", variant.rust_type(&self.file_and_mod())) } else { ("ref v", variant.rust_type(&self.file_and_mod()).ref_type()) }; w.case_block( format!("&{}({})", variant.path(&self.file_and_mod()), refv), |w| { cb( w, &variant, &RustValueTyped { value: "v".to_owned(), rust_type: vtype.clone(), }, ); }, ); } }); }, ); } } fn write_write_to_with_cached_sizes(&self, w: &mut CodeWriter) { let sig = format!( "write_to_with_cached_sizes(&self, os: &mut {protobuf_crate}::CodedOutputStream<'_>) -> {protobuf_crate}::Result<()>", protobuf_crate=protobuf_crate_path(&self.customize.for_elem), ); w.def_fn(&sig, |w| { // To have access to its methods but not polute the name space. for f in self.fields_except_oneof_and_group() { f.write_message_write_field("os", w); } self.write_match_each_oneof_variant(w, |w, variant, v| { variant .field .write_write_element(variant.elem(), w, "os", v); }); w.write_line("os.write_unknown_fields(self.special_fields.unknown_fields())?;"); w.write_line("::std::result::Result::Ok(())"); }); } fn write_default_instance_lazy(&self, w: &mut CodeWriter) { w.lazy_static_decl_get_simple( "instance", &format!("{}", self.rust_name()), &format!("{}::new", self.rust_name()), &format!("{}", protobuf_crate_path(&self.customize.for_elem)), ); } fn write_default_instance_static(&self, w: &mut CodeWriter) { w.stmt_block( &format!( "static instance: {} = {}", self.rust_name(), self.rust_name() ), |w| { for f in &self.fields_except_oneof_and_group() { w.field_entry( &f.rust_name.to_string(), &f.kind .default(&self.customize.for_elem, &self.file_and_mod(), true), ); } for o in &self.oneofs() { w.field_entry(&o.oneof.field_name().to_string(), EXPR_NONE); } w.field_entry( "special_fields", &format!( "{}::SpecialFields::new()", protobuf_crate_path(&self.customize.for_elem) ), ); }, ); w.write_line("&instance"); } fn write_default_instance(&self, w: &mut CodeWriter) { w.def_fn( &format!("default_instance() -> &'static {}", self.rust_name()), |w| { let has_map_field = self.fields.iter().any(|f| match f.kind { FieldKind::Map(..) => true, _ => false, }); if has_map_field { self.write_default_instance_lazy(w) } else { self.write_default_instance_static(w) } }, ); } fn write_compute_size(&self, w: &mut CodeWriter) { // Append sizes of messages in the tree to the specified vector. // First appended element is size of self, and then nested message sizes. // in serialization order are appended recursively."); w.comment("Compute sizes of nested messages"); // there are unused variables in oneof w.allow(&["unused_variables"]); w.def_fn("compute_size(&self) -> u64", |w| { // To have access to its methods but not polute the name space. w.write_line("let mut my_size = 0;"); for field in self.fields_except_oneof_and_group() { field.write_message_compute_field_size("my_size", w); } self.write_match_each_oneof_variant(w, |w, variant, v| { variant .field .write_element_size(variant.elem(), w, v, "my_size"); }); w.write_line(&format!( "my_size += {}::rt::unknown_fields_size(self.special_fields.unknown_fields());", protobuf_crate_path(&self.customize.for_elem) )); w.write_line("self.special_fields.cached_size().set(my_size as u32);"); w.write_line("my_size"); }); } fn write_field_accessors(&self, w: &mut CodeWriter) { for f in self.fields_except_group() { f.write_message_single_field_accessors(w); } } fn write_impl_self(&self, w: &mut CodeWriter) { w.impl_self_block(&format!("{}", self.rust_name()), |w| { w.pub_fn(&format!("new() -> {}", self.rust_name()), |w| { w.write_line("::std::default::Default::default()"); }); self.write_field_accessors(w); if !self.lite_runtime { w.write_line(""); self.write_generated_message_descriptor_data(w); } }); } fn write_unknown_fields(&self, w: &mut CodeWriter) { let sig = format!( "special_fields(&self) -> &{}::SpecialFields", protobuf_crate_path(&self.customize.for_elem) ); w.def_fn(&sig, |w| { w.write_line("&self.special_fields"); }); w.write_line(""); let sig = format!( "mut_special_fields(&mut self) -> &mut {}::SpecialFields", protobuf_crate_path(&self.customize.for_elem) ); w.def_fn(&sig, |w| { w.write_line("&mut self.special_fields"); }); } fn write_merge_from(&self, w: &mut CodeWriter) { let sig = format!( "merge_from(&mut self, is: &mut {}::CodedInputStream<'_>) -> {}::Result<()>", protobuf_crate_path(&self.customize.for_elem), protobuf_crate_path(&self.customize.for_elem), ); w.def_fn(&sig, |w| { w.while_block("let Some(tag) = is.read_raw_tag_or_eof()?", |w| { w.match_block("tag", |w| { for f in &self.fields_except_group() { f.write_merge_from_field_case_block(w); } w.case_block("tag", |w| { w.write_line(&format!("{}::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;", protobuf_crate_path(&self.customize.for_elem))); }); }); }); w.write_line("::std::result::Result::Ok(())"); }); } fn write_impl_message_full_fn_descriptor(&self, w: &mut CodeWriter) { write_fn_descriptor( &self.message.message, self.message.scope(), &self.customize.for_elem, w, ); } fn write_generated_message_descriptor_data(&self, w: &mut CodeWriter) { let sig = format!( "generated_message_descriptor_data() -> {}::reflect::GeneratedMessageDescriptorData", protobuf_crate_path(&self.customize.for_elem) ); w.fn_block( Visibility::Path(self.message.scope().rust_path_to_file().to_reverse()), &sig, |w| { let fields = self.fields_except_group(); let oneofs = self.oneofs(); w.write_line(&format!( "let mut fields = {};", expr_vec_with_capacity_const(fields.len()) )); w.write_line(&format!( "let mut oneofs = {};", expr_vec_with_capacity_const(oneofs.len()) )); for field in fields { field.write_push_accessor("fields", w); } for oneof in oneofs { w.write_line(&format!( "oneofs.push({}::generated_oneof_descriptor_data());", oneof.type_name_relative(&self.mod_name()) )); } w.write_line(&format!( "{}::reflect::GeneratedMessageDescriptorData::new_2::<{}>(", protobuf_crate_path(&self.customize.for_elem), self.rust_name(), )); w.indented(|w| { w.write_line(&format!("\"{}\",", self.message.name_to_package())); w.write_line("fields,"); w.write_line("oneofs,"); }); w.write_line(")"); }, ); } fn write_is_initialized(&self, w: &mut CodeWriter) { w.def_fn(&format!("is_initialized(&self) -> bool"), |w| { if !self.message.message.is_initialized_is_always_true() { // TODO: use single loop for f in self.required_fields() { f.write_if_self_field_is_none(w, |w| { w.write_line("return false;"); }); } for f in self.message_fields() { if let FieldKind::Map(..) = f.kind { // TODO w.comment("TODO: check map values are initialized"); continue; } f.write_for_self_field(w, "v", |w, _t| { w.if_stmt("!v.is_initialized()", |w| { w.write_line("return false;"); }); }); } } w.write_line("true"); }); } fn write_impl_message(&self, w: &mut CodeWriter) { w.impl_for_block( &format!("{}::Message", protobuf_crate_path(&self.customize.for_elem),), &format!("{}", self.rust_name()), |w| { w.write_line(&format!( "const NAME: &'static str = \"{}\";", self.message.message.name() )); w.write_line(""); self.write_is_initialized(w); w.write_line(""); self.write_merge_from(w); w.write_line(""); self.write_compute_size(w); w.write_line(""); self.write_write_to_with_cached_sizes(w); w.write_line(""); self.write_unknown_fields(w); w.write_line(""); w.def_fn(&format!("new() -> {}", self.rust_name()), |w| { w.write_line(&format!("{}::new()", self.rust_name())); }); w.write_line(""); w.def_fn("clear(&mut self)", |w| { for f in self.fields_except_group() { f.write_clear(w); } w.write_line("self.special_fields.clear();"); }); w.write_line(""); self.write_default_instance(w); }, ); } fn write_impl_message_full(&self, w: &mut CodeWriter) { w.impl_for_block( &format!( "{}::MessageFull", protobuf_crate_path(&self.customize.for_elem), ), &format!("{}", self.rust_name()), |w| { self.write_impl_message_full_fn_descriptor(w); }, ); } fn write_impl_value(&self, w: &mut CodeWriter) { w.impl_for_block( &format!( "{}::reflect::ProtobufValue", protobuf_crate_path(&self.customize.for_elem) ), &format!("{}", self.rust_name()), |w| { w.write_line(&format!( "type RuntimeType = {}::reflect::rt::RuntimeTypeMessage;", protobuf_crate_path(&self.customize.for_elem) )); }, ) } fn write_impl_display(&self, w: &mut CodeWriter) { w.impl_for_block( "::std::fmt::Display", &format!("{}", self.rust_name()), |w| { w.def_fn( "fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result", |w| { w.write_line(&format!( "{}::text_format::fmt(self, f)", protobuf_crate_path(&self.customize.for_elem) )); }, ); }, ); } fn supports_derive_partial_eq(&self) -> bool { // There's stack overflow in the compiler when struct has too many fields // https://github.com/rust-lang/rust/issues/40119 self.fields.len() <= 500 } fn write_struct(&self, w: &mut CodeWriter) { let mut derive = Vec::new(); if self.supports_derive_partial_eq() { derive.push("PartialEq"); } derive.extend(&["Clone", "Default", "Debug"]); w.derive(&derive); write_protoc_insertion_point_for_message( w, &self.customize.for_elem, &self.message_descriptor, ); w.pub_struct(&format!("{}", self.rust_name()), |w| { if !self.fields_except_oneof().is_empty() { w.comment("message fields"); for field in self.fields_except_oneof() { field.write_struct_field(w); } } if !self.oneofs().is_empty() { w.comment("message oneof groups"); for oneof in self.oneofs() { w.field_decl_vis( Visibility::Public, &oneof.oneof.field_name().to_string(), &oneof.full_storage_type().to_code(&self.customize.for_elem), ); } } w.comment("special fields"); let customize_special_fields = self .customize .child( &Customize::default(), &SpecialFieldPseudoDescriptor { message: &self.message.message, field: "special_fields", }, ) .for_elem; write_protoc_insertion_point_for_special_field( w, &customize_special_fields, &self.message_descriptor, "special_fields", ); w.pub_field_decl( "special_fields", &format!( "{}::SpecialFields", protobuf_crate_path(&self.customize.for_elem) ), ); }); } fn write_impl_default_for_amp(&self, w: &mut CodeWriter) { w.impl_args_for_block( &["'a"], "::std::default::Default", &format!("&'a {}", self.rust_name()), |w| { w.def_fn(&format!("default() -> &'a {}", self.rust_name()), |w| { w.write_line(&format!( "<{} as {}::Message>::default_instance()", self.rust_name(), protobuf_crate_path(&self.customize.for_elem), )); }); }, ); } fn write_dummy_impl_partial_eq(&self, w: &mut CodeWriter) { w.impl_for_block( "::std::cmp::PartialEq", &format!("{}", self.rust_name()), |w| { w.def_fn("eq(&self, _: &Self) -> bool", |w| { w.comment("https://github.com/rust-lang/rust/issues/40119"); w.unimplemented(); }); }, ); } pub fn write(&self, w: &mut CodeWriter) -> anyhow::Result<()> { w.all_documentation(self.info, self.path); self.write_struct(w); w.write_line(""); self.write_impl_default_for_amp(w); if !self.supports_derive_partial_eq() { w.write_line(""); self.write_dummy_impl_partial_eq(w); } w.write_line(""); self.write_impl_self(w); w.write_line(""); self.write_impl_message(w); if !self.lite_runtime { w.write_line(""); self.write_impl_message_full(w); } if !self.lite_runtime { w.write_line(""); self.write_impl_display(w); w.write_line(""); self.write_impl_value(w); } let mod_name = message_name_to_nested_mod_name(&self.message.message.name()); let oneofs = self.oneofs(); let nested_messages: Vec<_> = self .message .to_scope() .messages() .into_iter() .filter(|nested| { // ignore map entries, because they are not used in map fields !nested.is_map() }) .collect(); let nested_enums = self.message.to_scope().enums(); if !oneofs.is_empty() || !nested_messages.is_empty() || !nested_enums.is_empty() { w.write_line(""); w.write_line(&format!( "/// Nested message and enums of message `{}`", self.message.message.name() )); w.pub_mod(&mod_name.to_string(), |w| { let mut first = true; for oneof in &oneofs { w.write_line(""); oneof.write(w); } static NESTED_TYPE_NUMBER: protobuf::rt::Lazy = protobuf::rt::Lazy::new(); let nested_type_number = *NESTED_TYPE_NUMBER.get(|| { MessageDescriptor::for_type::() .field_by_name("nested_type") .expect("`nested_type` must exist") .proto() .number() }); let mut path = self.path.to_vec(); path.extend(&[nested_type_number, 0]); for (id, nested) in nested_messages.iter().enumerate() { let len = path.len() - 1; path[len] = id as i32; if !first { w.write_line(""); } first = false; MessageGen::new( &self.file_descriptor, nested, self.root_scope, &self.customize, &path, self.info, ) // TODO: do not unwrap. .unwrap() .write(w) // TODO: do not unwrap. .unwrap(); } static ENUM_TYPE_NUMBER: protobuf::rt::Lazy = protobuf::rt::Lazy::new(); let enum_type_number = *ENUM_TYPE_NUMBER.get(|| { MessageDescriptor::for_type::() .field_by_name("enum_type") .expect("`enum_type` must exist") .proto() .number() }); let len = path.len() - 2; path[len] = enum_type_number; for (id, enum_type) in self.message.to_scope().enums().iter().enumerate() { let len = path.len() - 1; path[len] = id as i32; if !first { w.write_line(""); } first = false; EnumGen::new( enum_type, &self.customize, self.root_scope, &path, self.info, ) .write(w); } }); } Ok(()) } } pub(crate) fn message_name_to_nested_mod_name(message_name: &str) -> RustIdent { let mod_name = snake_case(message_name); RustIdent::new(&mod_name) }