//! Oneof-related codegen functions. use std::collections::HashSet; use protobuf::descriptor::field_descriptor_proto; use protobuf::descriptor::file_options; use protobuf::reflect::FieldDescriptor; use protobuf_parse::ProtobufAbsPath; use crate::customize::ctx::CustomizeElemCtx; use crate::customize::Customize; use crate::gen::code_writer::CodeWriter; use crate::gen::code_writer::Visibility; use crate::gen::field::elem::FieldElem; use crate::gen::field::rust_variant_name_for_protobuf_oneof_field_name; use crate::gen::field::FieldGen; use crate::gen::file_and_mod::FileAndMod; use crate::gen::inside::protobuf_crate_path; use crate::gen::message::MessageGen; use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof; use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof_field; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust::path::RustPath; use crate::gen::rust::rel_path::RustRelativePath; use crate::gen::rust_types_values::make_path; use crate::gen::rust_types_values::RustType; use crate::gen::scope::OneofVariantWithContext; use crate::gen::scope::OneofWithContext; use crate::gen::scope::RootScope; use crate::gen::scope::WithScope; // oneof one { ... } #[derive(Clone)] pub(crate) struct OneofField<'a> { pub elem: FieldElem<'a>, pub oneof_variant_rust_name: RustIdent, pub oneof_field_name: RustIdent, pub type_name: RustIdentWithPath, pub boxed: bool, } impl<'a> OneofField<'a> { // Detecting recursion: if oneof fields contains a self-reference // or another message which has a reference to self, // put oneof variant into a box. fn need_boxed( field: &FieldDescriptor, root_scope: &RootScope, owner_name: &ProtobufAbsPath, ) -> bool { let mut visited_messages = HashSet::new(); let mut fields = vec![field.clone()]; while let Some(field) = fields.pop() { if field.proto().type_() == field_descriptor_proto::Type::TYPE_MESSAGE { let message_name = ProtobufAbsPath::from(field.proto().type_name()); if !visited_messages.insert(message_name.clone()) { continue; } if message_name == *owner_name { return true; } let message = root_scope.find_message(&message_name); fields.extend( message .message .fields() .into_iter() .filter(|f| f.containing_oneof().is_some()), ); } } false } pub fn parse( oneof: &OneofWithContext<'a>, field: &FieldDescriptor, elem: FieldElem<'a>, root_scope: &RootScope, ) -> OneofField<'a> { let boxed = OneofField::need_boxed(field, root_scope, &oneof.message.name_absolute()); OneofField { elem, type_name: oneof.rust_name(), boxed, oneof_variant_rust_name: rust_variant_name_for_protobuf_oneof_field_name(field.name()), oneof_field_name: oneof.field_name(), } } pub fn rust_type(&self, reference: &FileAndMod) -> RustType { let t = self.elem.rust_storage_elem_type(reference); if self.boxed { RustType::Uniq(Box::new(t)) } else { t } } pub fn variant_path(&self, reference: &RustRelativePath) -> RustIdentWithPath { make_path( reference, &self .type_name .to_path() .with_ident(self.oneof_variant_rust_name.clone()), ) } } #[derive(Clone)] pub(crate) struct OneofVariantGen<'a> { oneof: &'a OneofGen<'a>, _variant: OneofVariantWithContext<'a>, oneof_field: OneofField<'a>, pub field: FieldGen<'a>, _path: String, } impl<'a> OneofVariantGen<'a> { fn parse( oneof: &'a OneofGen<'a>, variant: OneofVariantWithContext<'a>, field: &'a FieldGen, _root_scope: &RootScope, ) -> OneofVariantGen<'a> { OneofVariantGen { oneof, _variant: variant.clone(), field: field.clone(), _path: format!( "{}::{}", oneof.type_name_relative(&oneof.oneof.message.scope.rust_path_to_file()), field.rust_name ), oneof_field: OneofField::parse( variant.oneof, &field.proto_field.field, field.elem().clone(), oneof.message.root_scope, ), } } pub fn rust_type(&self, reference: &FileAndMod) -> RustType { self.oneof_field.rust_type(reference) } pub fn path(&self, reference: &FileAndMod) -> RustPath { RustPath::from(format!( "{}::{}", self.oneof.type_name_relative(&reference.relative_mod), self.oneof_field.oneof_variant_rust_name, )) } pub(crate) fn elem(&self) -> &FieldElem<'_> { self.field.elem() } } pub(crate) struct OneofGen<'a> { // Message containing this oneof message: &'a MessageGen<'a>, pub oneof: OneofWithContext<'a>, customize: CustomizeElemCtx<'a>, lite_runtime: bool, } impl<'a> OneofGen<'a> { pub fn parse( message: &'a MessageGen, oneof: OneofWithContext<'a>, parent_customize: &CustomizeElemCtx<'a>, ) -> OneofGen<'a> { let customize = parent_customize.child(&Customize::default(), &oneof.oneof); let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| { oneof .message .file_descriptor() .proto() .options .optimize_for() == file_options::OptimizeMode::LITE_RUNTIME }); OneofGen { message, oneof, customize, lite_runtime, } } pub fn type_name_relative(&self, source: &RustRelativePath) -> RustIdentWithPath { make_path(source, &self.oneof.rust_name()) } pub fn variants_except_group(&'a self) -> Vec> { self.oneof .variants() .into_iter() .filter_map(|v| { let field = self .message .fields .iter() .filter(|f| f.proto_field.name() == v.field.name()) .next() .expect(&format!("field not found by name: {}", v.field.name())); match field.proto_type { field_descriptor_proto::Type::TYPE_GROUP => None, _ => Some(OneofVariantGen::parse( self, v, field, self.message.root_scope, )), } }) .collect() } pub fn full_storage_type(&self) -> RustType { RustType::Option(Box::new(RustType::Oneof( self.type_name_relative( &self .oneof .message .scope .file_and_mod(self.customize.for_elem.clone()) .relative_mod, ) .clone(), ))) } fn file_and_mod(&self) -> FileAndMod { let mut file_and_mod = self .message .message .scope .file_and_mod(self.customize.for_elem.clone()); file_and_mod .relative_mod .push_ident(self.message.message.mod_name()); file_and_mod } fn write_enum(&self, w: &mut CodeWriter) { let derive = vec!["Clone", "PartialEq", "Debug"]; w.derive(&derive); w.write_line("#[non_exhaustive]"); write_protoc_insertion_point_for_oneof(w, &self.customize.for_elem, &self.oneof.oneof); w.pub_enum(&self.oneof.rust_name().ident.to_string(), |w| { for variant in self.variants_except_group() { write_protoc_insertion_point_for_oneof_field( w, &self.customize.for_children, &variant.field.proto_field.field, ); w.write_line(&format!( "{}({}),", variant.oneof_field.oneof_variant_rust_name, &variant .rust_type(&self.file_and_mod()) .to_code(&self.customize.for_elem) )); } }); } fn write_impl_oneof(&self, w: &mut CodeWriter) { w.impl_for_block( &format!("{}::Oneof", protobuf_crate_path(&self.customize.for_elem)), self.oneof.rust_name().ident.to_string(), |_w| { // nothing here yet }, ); } fn write_impl_oneof_full_fn_descriptor(&self, w: &mut CodeWriter) { let sig = format!( "descriptor() -> {}::reflect::OneofDescriptor", protobuf_crate_path(&self.customize.for_elem), ); w.def_fn(&sig, |w| { w.lazy_static( "descriptor", &format!( "{}::reflect::OneofDescriptor", protobuf_crate_path(&self.customize.for_elem), ), &protobuf_crate_path(&self.customize.for_elem).to_string(), ); let message_type = make_path( &self .oneof .message .scope() .rust_path_to_file() .append(self.oneof.message.mod_name().into_rel_path()), &self.oneof.message.rust_name_to_file(), ); let expr = format!( "<{} as {}::MessageFull>::descriptor().oneof_by_name(\"{}\").unwrap()", message_type, protobuf_crate_path(&self.customize.for_elem), self.oneof.oneof.name() ); w.write_line(&format!("descriptor.get(|| {}).clone()", expr)); }); } fn write_impl_oneof_full(&self, w: &mut CodeWriter) { w.impl_for_block( &format!( "{}::OneofFull", protobuf_crate_path(&self.customize.for_elem) ), self.oneof.rust_name().ident.to_string(), |w| self.write_impl_oneof_full_fn_descriptor(w), ) } fn write_generated_oneof_descriptor_data(&self, w: &mut CodeWriter) { let sig = format!( "generated_oneof_descriptor_data() -> {}::reflect::GeneratedOneofDescriptorData", protobuf_crate_path(&self.customize.for_elem) ); w.fn_block( Visibility::Path( self.oneof .rust_name() .path .into_relative_or_panic() .to_reverse(), ), &sig, |w| { w.write_line(&format!( "{}::reflect::GeneratedOneofDescriptorData::new::<{}>(\"{}\")", protobuf_crate_path(&self.customize.for_elem), &self.oneof.rust_name().ident, self.oneof.oneof.name(), )); }, ); } fn write_impl_self(&self, w: &mut CodeWriter) { w.impl_self_block(&format!("{}", &self.oneof.rust_name().ident), |w| { if !self.lite_runtime { self.write_generated_oneof_descriptor_data(w); } }); } pub fn write(&self, w: &mut CodeWriter) { self.write_enum(w); w.write_line(""); self.write_impl_oneof(w); if !self.lite_runtime { w.write_line(""); self.write_impl_oneof_full(w); } w.write_line(""); self.write_impl_self(w); } }