protobuf/reflect/file/
building.rs
1use std::collections::HashMap;
2use std::iter;
3
4use crate::descriptor::field_descriptor_proto;
5use crate::descriptor::DescriptorProto;
6use crate::descriptor::EnumDescriptorProto;
7use crate::descriptor::FieldDescriptorProto;
8use crate::descriptor::FileDescriptorProto;
9use crate::reflect::error::ReflectError;
10use crate::reflect::field::index::ForwardProtobufFieldType;
11use crate::reflect::field::index::ForwardProtobufTypeBox;
12use crate::reflect::file::index::MessageIndices;
13use crate::reflect::find_message_or_enum::find_message_or_enum;
14use crate::reflect::find_message_or_enum::MessageOrEnum;
15use crate::reflect::name::protobuf_name_starts_with_package;
16use crate::reflect::runtime_type_box::RuntimeType;
17use crate::reflect::FileDescriptor;
18
19pub(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
27impl<'a> FileDescriptorBuilding<'a> {
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
33 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
56 fn all_files_str(&self) -> String {
57 self.all_descriptors()
58 .map(|d| d.name())
59 .collect::<Vec<_>>()
60 .join(", ")
61 }
62
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
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
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
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 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}