1 use std::collections::HashSet;
2 use std::hash::Hash;
3 use std::hash::Hasher;
4 use std::sync::Arc;
5 
6 use crate::descriptor::DescriptorProto;
7 use crate::descriptor::FileDescriptorProto;
8 use crate::reflect::file::dynamic::DynamicFileDescriptor;
9 use crate::reflect::file::fds::build_fds;
10 use crate::reflect::file::index::EnumIndices;
11 use crate::reflect::file::index::FileDescriptorCommon;
12 use crate::reflect::file::index::MessageIndices;
13 use crate::reflect::name::protobuf_name_starts_with_package;
14 use crate::reflect::service::ServiceDescriptor;
15 use crate::reflect::EnumDescriptor;
16 use crate::reflect::FieldDescriptor;
17 use crate::reflect::GeneratedFileDescriptor;
18 use crate::reflect::MessageDescriptor;
19 use crate::reflect::Syntax;
20 
21 pub(crate) mod building;
22 pub(crate) mod dynamic;
23 pub(crate) mod fds;
24 pub(crate) mod generated;
25 pub(crate) mod index;
26 pub(crate) mod syntax;
27 
28 #[derive(Clone, Debug)]
29 pub(crate) enum FileDescriptorImpl {
30     Generated(&'static GeneratedFileDescriptor),
31     Dynamic(Arc<DynamicFileDescriptor>),
32 }
33 
34 impl PartialEq for FileDescriptorImpl {
eq(&self, other: &Self) -> bool35     fn eq(&self, other: &Self) -> bool {
36         match (self, other) {
37             (FileDescriptorImpl::Generated(a), FileDescriptorImpl::Generated(b)) => {
38                 *a as *const GeneratedFileDescriptor == *b as *const GeneratedFileDescriptor
39             }
40             (FileDescriptorImpl::Dynamic(a), FileDescriptorImpl::Dynamic(b)) => Arc::ptr_eq(a, b),
41             _ => false,
42         }
43     }
44 }
45 
46 impl Hash for FileDescriptorImpl {
hash<H: Hasher>(&self, state: &mut H)47     fn hash<H: Hasher>(&self, state: &mut H) {
48         match self {
49             FileDescriptorImpl::Generated(g) => {
50                 Hash::hash(&(*g as *const GeneratedFileDescriptor), state)
51             }
52             FileDescriptorImpl::Dynamic(a) => {
53                 Hash::hash(&(&**a as *const DynamicFileDescriptor), state)
54             }
55         }
56     }
57 }
58 
59 impl Eq for FileDescriptorImpl {}
60 
61 /// Reflection for objects defined in `.proto` file (messages, enums, etc).
62 ///
63 /// The object is refcounted: clone is shallow.
64 ///
65 /// The equality performs pointer comparison: two clones of the same `FileDescriptor`
66 /// objects are equal, but two `FileDescriptor` objects created from the same `FileDescriptorProto`
67 /// objects are **not** equal.
68 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
69 pub struct FileDescriptor {
70     pub(crate) imp: FileDescriptorImpl,
71 }
72 
73 impl FileDescriptor {
common(&self) -> &FileDescriptorCommon74     pub(crate) fn common(&self) -> &FileDescriptorCommon {
75         match &self.imp {
76             FileDescriptorImpl::Generated(g) => &g.common,
77             FileDescriptorImpl::Dynamic(d) => &d.common,
78         }
79     }
80 
81     /// Same as `common`, but returns `&'static`.
common_for_generated_descriptor(&self) -> &'static FileDescriptorCommon82     pub(crate) fn common_for_generated_descriptor(&self) -> &'static FileDescriptorCommon {
83         match &self.imp {
84             FileDescriptorImpl::Generated(g) => &g.common,
85             FileDescriptorImpl::Dynamic(..) => panic!("not generated"),
86         }
87     }
88 
message_indices(&self, index: usize) -> &MessageIndices89     pub(crate) fn message_indices(&self, index: usize) -> &MessageIndices {
90         &self.common().messages[index]
91     }
92 
message_by_index(&self, index: usize) -> MessageDescriptor93     pub(crate) fn message_by_index(&self, index: usize) -> MessageDescriptor {
94         MessageDescriptor {
95             file_descriptor: self.clone(),
96             index,
97         }
98     }
99 
message_proto_by_index(&self, index: usize) -> &DescriptorProto100     pub(crate) fn message_proto_by_index(&self, index: usize) -> &DescriptorProto {
101         &self.common().messages[index].proto
102     }
103 
enum_indices(&self, index: usize) -> &EnumIndices104     pub(crate) fn enum_indices(&self, index: usize) -> &EnumIndices {
105         &self.common().enums[index]
106     }
107 
108     /// The file name.
name(&self) -> &str109     pub fn name(&self) -> &str {
110         self.proto().name()
111     }
112 
113     /// Protobuf package.
package(&self) -> &str114     pub fn package(&self) -> &str {
115         self.proto().package()
116     }
117 
118     /// Syntax of current file.
syntax(&self) -> Syntax119     pub fn syntax(&self) -> Syntax {
120         Syntax::parse(self.proto().syntax()).unwrap_or(Syntax::Proto2)
121     }
122 
123     /// Top-level messages.
messages(&self) -> impl Iterator<Item = MessageDescriptor> + '_124     pub fn messages(&self) -> impl Iterator<Item = MessageDescriptor> + '_ {
125         self.common()
126             .top_level_messages
127             .iter()
128             .map(|i| MessageDescriptor::new(self.clone(), *i))
129     }
130 
131     /// Get top-level enums.
enums(&self) -> impl Iterator<Item = EnumDescriptor> + '_132     pub fn enums(&self) -> impl Iterator<Item = EnumDescriptor> + '_ {
133         self.proto()
134             .enum_type
135             .iter()
136             .enumerate()
137             .map(|(i, _)| EnumDescriptor::new(self.clone(), i))
138     }
139 
140     /// Get services defined in `.proto` file.
services(&self) -> impl Iterator<Item = ServiceDescriptor> + '_141     pub fn services(&self) -> impl Iterator<Item = ServiceDescriptor> + '_ {
142         self.proto()
143             .service
144             .iter()
145             .enumerate()
146             .map(|(i, _)| ServiceDescriptor::new(self.clone(), i))
147     }
148 
149     /// Extension fields.
extensions(&self) -> impl Iterator<Item = FieldDescriptor> + '_150     pub fn extensions(&self) -> impl Iterator<Item = FieldDescriptor> + '_ {
151         self.common()
152             .extension_field_range()
153             .map(move |index| FieldDescriptor {
154                 file_descriptor: self.clone(),
155                 index,
156             })
157     }
158 
159     /// Find message by name relative to the package.
160     ///
161     /// Only search in the current file, not in any dependencies.
message_by_package_relative_name(&self, name: &str) -> Option<MessageDescriptor>162     pub fn message_by_package_relative_name(&self, name: &str) -> Option<MessageDescriptor> {
163         self.common()
164             .message_by_name_to_package
165             .get(name)
166             .map(|&index| MessageDescriptor::new(self.clone(), index))
167     }
168 
169     /// Find message by name relative to the package.
170     ///
171     /// Only search in the current file, not in any dependencies.
enum_by_package_relative_name(&self, name: &str) -> Option<EnumDescriptor>172     pub fn enum_by_package_relative_name(&self, name: &str) -> Option<EnumDescriptor> {
173         self.common()
174             .enums_by_name_to_package
175             .get(name)
176             .map(|&index| EnumDescriptor::new(self.clone(), index))
177     }
178 
179     /// Find message by fully-qualified name.
180     ///
181     /// Only search in the current file, not in any dependencies.
message_by_full_name(&self, name: &str) -> Option<MessageDescriptor>182     pub fn message_by_full_name(&self, name: &str) -> Option<MessageDescriptor> {
183         if let Some(name_to_package) =
184             protobuf_name_starts_with_package(name, self.proto().package())
185         {
186             self.message_by_package_relative_name(name_to_package)
187         } else {
188             None
189         }
190     }
191 
192     /// Find enum by name fully-qualified name.
193     ///
194     /// Only search in the current file, not in any dependencies.
enum_by_full_name(&self, name: &str) -> Option<EnumDescriptor>195     pub fn enum_by_full_name(&self, name: &str) -> Option<EnumDescriptor> {
196         if let Some(name_to_package) =
197             protobuf_name_starts_with_package(name, self.proto().package())
198         {
199             self.enum_by_package_relative_name(name_to_package)
200         } else {
201             None
202         }
203     }
204 
205     /// This function is called from generated code, it is not stable, and should not be called.
206     #[doc(hidden)]
new_generated_2(generated: &'static GeneratedFileDescriptor) -> FileDescriptor207     pub fn new_generated_2(generated: &'static GeneratedFileDescriptor) -> FileDescriptor {
208         FileDescriptor {
209             imp: FileDescriptorImpl::Generated(generated),
210         }
211     }
212 
213     /// Dynamic message created from [`FileDescriptorProto`] without generated files.
new_dynamic( proto: FileDescriptorProto, dependencies: &[FileDescriptor], ) -> crate::Result<FileDescriptor>214     pub fn new_dynamic(
215         proto: FileDescriptorProto,
216         dependencies: &[FileDescriptor],
217     ) -> crate::Result<FileDescriptor> {
218         Ok(FileDescriptor {
219             imp: FileDescriptorImpl::Dynamic(Arc::new(DynamicFileDescriptor::new(
220                 proto,
221                 dependencies,
222             )?)),
223         })
224     }
225 
226     /// Create a set of file descriptors from individual file descriptors.
new_dynamic_fds( protos: Vec<FileDescriptorProto>, dependencies: &[FileDescriptor], ) -> crate::Result<Vec<FileDescriptor>>227     pub fn new_dynamic_fds(
228         protos: Vec<FileDescriptorProto>,
229         dependencies: &[FileDescriptor],
230     ) -> crate::Result<Vec<FileDescriptor>> {
231         build_fds(protos, dependencies)
232     }
233 
234     /// `.proto` data for this file.
proto(&self) -> &FileDescriptorProto235     pub fn proto(&self) -> &FileDescriptorProto {
236         match &self.imp {
237             FileDescriptorImpl::Generated(g) => &g.proto,
238             FileDescriptorImpl::Dynamic(d) => &d.proto,
239         }
240     }
241 
242     /// Direct dependencies of this file.
deps(&self) -> &[FileDescriptor]243     pub fn deps(&self) -> &[FileDescriptor] {
244         &self.common().dependencies
245     }
246 
247     /// Subset of dependencies which are public
public_deps(&self) -> impl Iterator<Item = FileDescriptor> + '_248     pub fn public_deps(&self) -> impl Iterator<Item = FileDescriptor> + '_ {
249         self.proto()
250             .public_dependency
251             .iter()
252             .map(|&i| self.deps()[i as usize].clone())
253     }
254 
_all_files(&self) -> Vec<&FileDescriptor>255     fn _all_files(&self) -> Vec<&FileDescriptor> {
256         let mut r = Vec::new();
257         let mut visited = HashSet::new();
258 
259         let mut stack = Vec::new();
260         stack.push(self);
261         while let Some(file) = stack.pop() {
262             if !visited.insert(file) {
263                 continue;
264             }
265 
266             r.push(file);
267             stack.extend(file.deps());
268         }
269 
270         r
271     }
272 }
273 
274 #[cfg(test)]
275 mod test {
276     use crate::descriptor;
277 
278     #[test]
279     #[cfg_attr(miri, ignore)]
eq()280     fn eq() {
281         assert!(descriptor::file_descriptor() == &descriptor::file_descriptor().clone());
282     }
283 }
284