1 use std::collections::HashMap;
2 use std::iter;
3 
4 use crate::descriptor::field_descriptor_proto;
5 use crate::descriptor::DescriptorProto;
6 use crate::descriptor::EnumDescriptorProto;
7 use crate::descriptor::FieldDescriptorProto;
8 use crate::descriptor::FileDescriptorProto;
9 use crate::reflect::error::ReflectError;
10 use crate::reflect::field::index::ForwardProtobufFieldType;
11 use crate::reflect::field::index::ForwardProtobufTypeBox;
12 use crate::reflect::file::index::MessageIndices;
13 use crate::reflect::find_message_or_enum::find_message_or_enum;
14 use crate::reflect::find_message_or_enum::MessageOrEnum;
15 use crate::reflect::name::protobuf_name_starts_with_package;
16 use crate::reflect::runtime_type_box::RuntimeType;
17 use crate::reflect::FileDescriptor;
18 
19 pub(crate) struct FileDescriptorBuilding<'a> {
20     pub(crate) current_file_descriptor: &'a FileDescriptorProto,
21     pub(crate) deps_with_public: &'a [FileDescriptor],
22     pub(crate) message_by_name_to_package: &'a HashMap<String, usize>,
23     pub(crate) messages: &'a [MessageIndices],
24     pub(crate) enums_by_name_to_package: &'a HashMap<String, usize>,
25 }
26 
27 impl<'a> FileDescriptorBuilding<'a> {
all_descriptors(&self) -> impl Iterator<Item = &'a FileDescriptorProto>28     fn all_descriptors(&self) -> impl Iterator<Item = &'a FileDescriptorProto> {
29         iter::once(self.current_file_descriptor)
30             .chain(self.deps_with_public.iter().map(|d| d.proto()))
31     }
32 
find_enum(&self, full_name: &str) -> &'a EnumDescriptorProto33     pub fn find_enum(&self, full_name: &str) -> &'a EnumDescriptorProto {
34         assert!(full_name.starts_with("."));
35 
36         for file in self.all_descriptors() {
37             if let Some(name_to_package) =
38                 protobuf_name_starts_with_package(full_name, file.package())
39             {
40                 if let Some((_, me)) = find_message_or_enum(file, name_to_package) {
41                     match me {
42                         MessageOrEnum::Enum(e) => return e,
43                         MessageOrEnum::Message(_) => panic!("not an enum: {}", full_name),
44                     }
45                 }
46             }
47         }
48 
49         panic!(
50             "enum not found: {}, in files: {}",
51             full_name,
52             self.all_files_str()
53         );
54     }
55 
all_files_str(&self) -> String56     fn all_files_str(&self) -> String {
57         self.all_descriptors()
58             .map(|d| d.name())
59             .collect::<Vec<_>>()
60             .join(", ")
61     }
62 
resolve_field_type( &self, field: &FieldDescriptorProto, ) -> crate::Result<ForwardProtobufFieldType>63     pub(crate) fn resolve_field_type(
64         &self,
65         field: &FieldDescriptorProto,
66     ) -> crate::Result<ForwardProtobufFieldType> {
67         Ok(match field.label() {
68             field_descriptor_proto::Label::LABEL_OPTIONAL
69             | field_descriptor_proto::Label::LABEL_REQUIRED => {
70                 ForwardProtobufFieldType::Singular(self.resolve_field_element_type(field)?)
71             }
72             field_descriptor_proto::Label::LABEL_REPEATED => {
73                 let element = self.resolve_field_element_type(field)?;
74                 let type_proto = match &element {
75                     ForwardProtobufTypeBox::CurrentFileMessage(m) => {
76                         Some(&*self.messages[*m].proto)
77                     }
78                     ForwardProtobufTypeBox::ProtobufTypeBox(t) => match t.runtime() {
79                         RuntimeType::Message(m) => Some(m.proto()),
80                         _ => None,
81                     },
82                     _ => None,
83                 };
84                 match type_proto {
85                     Some(m) if m.options.get_or_default().map_entry() => self.map_field(m)?,
86                     _ => ForwardProtobufFieldType::Repeated(element),
87                 }
88             }
89         })
90     }
91 
resolve_field_element_type( &self, field: &FieldDescriptorProto, ) -> crate::Result<ForwardProtobufTypeBox>92     fn resolve_field_element_type(
93         &self,
94         field: &FieldDescriptorProto,
95     ) -> crate::Result<ForwardProtobufTypeBox> {
96         Ok(match field.type_() {
97             field_descriptor_proto::Type::TYPE_MESSAGE
98             | field_descriptor_proto::Type::TYPE_GROUP => {
99                 self.resolve_message(field.type_name())?
100             }
101             field_descriptor_proto::Type::TYPE_ENUM => {
102                 if let Some(name_to_package) = protobuf_name_starts_with_package(
103                     field.type_name(),
104                     self.current_file_descriptor.package(),
105                 ) {
106                     if let Some(index) = self.enums_by_name_to_package.get(name_to_package) {
107                         return Ok(ForwardProtobufTypeBox::CurrentFileEnum(*index));
108                     }
109                 }
110                 for dep in self.deps_with_public {
111                     if let Some(m) = dep.enum_by_full_name(field.type_name()) {
112                         return Ok(ForwardProtobufTypeBox::enumeration(m));
113                     }
114                 }
115                 panic!(
116                     "enum not found: {}; files: {}",
117                     field.type_name(),
118                     self.all_files_str()
119                 );
120             }
121             t => ForwardProtobufTypeBox::from_proto_type(t),
122         })
123     }
124 
resolve_message(&self, type_name: &str) -> crate::Result<ForwardProtobufTypeBox>125     pub(crate) fn resolve_message(&self, type_name: &str) -> crate::Result<ForwardProtobufTypeBox> {
126         if let Some(name_to_package) =
127             protobuf_name_starts_with_package(type_name, self.current_file_descriptor.package())
128         {
129             if let Some(index) = self.message_by_name_to_package.get(name_to_package) {
130                 return Ok(ForwardProtobufTypeBox::CurrentFileMessage(*index));
131             }
132         }
133         for dep in self.deps_with_public {
134             if let Some(m) = dep.message_by_full_name(type_name) {
135                 return Ok(ForwardProtobufTypeBox::message(m));
136             }
137         }
138         Err(ReflectError::MessageNotFoundInFiles(type_name.to_owned(), self.all_files_str()).into())
139     }
140 
map_field(&self, type_proto: &DescriptorProto) -> crate::Result<ForwardProtobufFieldType>141     fn map_field(&self, type_proto: &DescriptorProto) -> crate::Result<ForwardProtobufFieldType> {
142         assert!(type_proto.name().ends_with("Entry"));
143 
144         assert_eq!(0, type_proto.extension.len());
145         assert_eq!(0, type_proto.extension_range.len());
146         assert_eq!(0, type_proto.nested_type.len());
147         assert_eq!(0, type_proto.enum_type.len());
148 
149         assert_eq!(2, type_proto.field.len());
150         let key = &type_proto.field[0];
151         let value = &type_proto.field[1];
152 
153         assert_eq!("key", key.name());
154         assert_eq!("value", value.name());
155 
156         assert_eq!(1, key.number());
157         assert_eq!(2, value.number());
158 
159         assert_eq!(field_descriptor_proto::Label::LABEL_OPTIONAL, key.label());
160         assert_eq!(field_descriptor_proto::Label::LABEL_OPTIONAL, value.label());
161 
162         // It is OK to resolve using current descriptor because map field
163         // should always point to the same file.
164         let key = self.resolve_field_element_type(key)?;
165         let value = self.resolve_field_element_type(value)?;
166         Ok(ForwardProtobufFieldType::Map(key, value))
167     }
168 }
169