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