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        // 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}