use std::collections::HashMap; use std::iter; use crate::descriptor::field_descriptor_proto; use crate::descriptor::DescriptorProto; use crate::descriptor::EnumDescriptorProto; use crate::descriptor::FieldDescriptorProto; use crate::descriptor::FileDescriptorProto; use crate::reflect::error::ReflectError; use crate::reflect::field::index::ForwardProtobufFieldType; use crate::reflect::field::index::ForwardProtobufTypeBox; use crate::reflect::file::index::MessageIndices; use crate::reflect::find_message_or_enum::find_message_or_enum; use crate::reflect::find_message_or_enum::MessageOrEnum; use crate::reflect::name::protobuf_name_starts_with_package; use crate::reflect::runtime_type_box::RuntimeType; use crate::reflect::FileDescriptor; pub(crate) struct FileDescriptorBuilding<'a> { pub(crate) current_file_descriptor: &'a FileDescriptorProto, pub(crate) deps_with_public: &'a [FileDescriptor], pub(crate) message_by_name_to_package: &'a HashMap, pub(crate) messages: &'a [MessageIndices], pub(crate) enums_by_name_to_package: &'a HashMap, } impl<'a> FileDescriptorBuilding<'a> { fn all_descriptors(&self) -> impl Iterator { iter::once(self.current_file_descriptor) .chain(self.deps_with_public.iter().map(|d| d.proto())) } pub fn find_enum(&self, full_name: &str) -> &'a EnumDescriptorProto { assert!(full_name.starts_with(".")); for file in self.all_descriptors() { if let Some(name_to_package) = protobuf_name_starts_with_package(full_name, file.package()) { if let Some((_, me)) = find_message_or_enum(file, name_to_package) { match me { MessageOrEnum::Enum(e) => return e, MessageOrEnum::Message(_) => panic!("not an enum: {}", full_name), } } } } panic!( "enum not found: {}, in files: {}", full_name, self.all_files_str() ); } fn all_files_str(&self) -> String { self.all_descriptors() .map(|d| d.name()) .collect::>() .join(", ") } pub(crate) fn resolve_field_type( &self, field: &FieldDescriptorProto, ) -> crate::Result { Ok(match field.label() { field_descriptor_proto::Label::LABEL_OPTIONAL | field_descriptor_proto::Label::LABEL_REQUIRED => { ForwardProtobufFieldType::Singular(self.resolve_field_element_type(field)?) } field_descriptor_proto::Label::LABEL_REPEATED => { let element = self.resolve_field_element_type(field)?; let type_proto = match &element { ForwardProtobufTypeBox::CurrentFileMessage(m) => { Some(&*self.messages[*m].proto) } ForwardProtobufTypeBox::ProtobufTypeBox(t) => match t.runtime() { RuntimeType::Message(m) => Some(m.proto()), _ => None, }, _ => None, }; match type_proto { Some(m) if m.options.get_or_default().map_entry() => self.map_field(m)?, _ => ForwardProtobufFieldType::Repeated(element), } } }) } fn resolve_field_element_type( &self, field: &FieldDescriptorProto, ) -> crate::Result { Ok(match field.type_() { field_descriptor_proto::Type::TYPE_MESSAGE | field_descriptor_proto::Type::TYPE_GROUP => { self.resolve_message(field.type_name())? } field_descriptor_proto::Type::TYPE_ENUM => { if let Some(name_to_package) = protobuf_name_starts_with_package( field.type_name(), self.current_file_descriptor.package(), ) { if let Some(index) = self.enums_by_name_to_package.get(name_to_package) { return Ok(ForwardProtobufTypeBox::CurrentFileEnum(*index)); } } for dep in self.deps_with_public { if let Some(m) = dep.enum_by_full_name(field.type_name()) { return Ok(ForwardProtobufTypeBox::enumeration(m)); } } panic!( "enum not found: {}; files: {}", field.type_name(), self.all_files_str() ); } t => ForwardProtobufTypeBox::from_proto_type(t), }) } pub(crate) fn resolve_message(&self, type_name: &str) -> crate::Result { if let Some(name_to_package) = protobuf_name_starts_with_package(type_name, self.current_file_descriptor.package()) { if let Some(index) = self.message_by_name_to_package.get(name_to_package) { return Ok(ForwardProtobufTypeBox::CurrentFileMessage(*index)); } } for dep in self.deps_with_public { if let Some(m) = dep.message_by_full_name(type_name) { return Ok(ForwardProtobufTypeBox::message(m)); } } Err(ReflectError::MessageNotFoundInFiles(type_name.to_owned(), self.all_files_str()).into()) } fn map_field(&self, type_proto: &DescriptorProto) -> crate::Result { assert!(type_proto.name().ends_with("Entry")); assert_eq!(0, type_proto.extension.len()); assert_eq!(0, type_proto.extension_range.len()); assert_eq!(0, type_proto.nested_type.len()); assert_eq!(0, type_proto.enum_type.len()); assert_eq!(2, type_proto.field.len()); let key = &type_proto.field[0]; let value = &type_proto.field[1]; assert_eq!("key", key.name()); assert_eq!("value", value.name()); assert_eq!(1, key.number()); assert_eq!(2, value.number()); assert_eq!(field_descriptor_proto::Label::LABEL_OPTIONAL, key.label()); assert_eq!(field_descriptor_proto::Label::LABEL_OPTIONAL, value.label()); // It is OK to resolve using current descriptor because map field // should always point to the same file. let key = self.resolve_field_element_type(key)?; let value = self.resolve_field_element_type(value)?; Ok(ForwardProtobufFieldType::Map(key, value)) } }