use std::collections::HashMap; use std::marker; use crate::descriptor::DescriptorProto; use crate::descriptor::FileDescriptorProto; use crate::descriptorx::find_message_by_rust_name; use crate::reflect::acc::FieldAccessor; use crate::reflect::find_message_or_enum::find_message_or_enum; use crate::reflect::find_message_or_enum::MessageOrEnum; use crate::reflect::FieldDescriptor; use crate::Message; trait MessageFactory: Send + Sync + 'static { fn new_instance(&self) -> Box; } struct MessageFactoryImpl(marker::PhantomData); impl MessageFactory for MessageFactoryImpl where M: 'static + Message + Default + Clone + PartialEq, { fn new_instance(&self) -> Box { let m: M = Default::default(); Box::new(m) } } /// Dynamic message type pub struct MessageDescriptor { full_name: String, proto: &'static DescriptorProto, factory: &'static dyn MessageFactory, fields: Vec, index_by_name: HashMap, index_by_name_or_json_name: HashMap, index_by_number: HashMap, } impl MessageDescriptor { /// Get underlying `DescriptorProto` object. pub fn get_proto(&self) -> &DescriptorProto { self.proto } /// Get a message descriptor for given message type pub fn for_type() -> &'static MessageDescriptor { M::descriptor_static() } fn compute_full_name(package: &str, path_to_package: &str, proto: &DescriptorProto) -> String { let mut full_name = package.to_owned(); if path_to_package.len() != 0 { if full_name.len() != 0 { full_name.push('.'); } full_name.push_str(path_to_package); } if full_name.len() != 0 { full_name.push('.'); } full_name.push_str(proto.get_name()); full_name } // Non-generic part of `new` is a separate function // to reduce code bloat from multiple instantiations. fn new_non_generic_by_rust_name( rust_name: &'static str, fields: Vec, file: &'static FileDescriptorProto, factory: &'static dyn MessageFactory, ) -> MessageDescriptor { let proto = find_message_by_rust_name(file, rust_name); let mut field_proto_by_name = HashMap::new(); for field_proto in proto.message.get_field() { field_proto_by_name.insert(field_proto.get_name(), field_proto); } let mut index_by_name = HashMap::new(); let mut index_by_name_or_json_name = HashMap::new(); let mut index_by_number = HashMap::new(); let mut full_name = file.get_package().to_string(); if full_name.len() > 0 { full_name.push('.'); } full_name.push_str(proto.message.get_name()); let fields: Vec<_> = fields .into_iter() .map(|f| { let proto = *field_proto_by_name.get(&f.name).unwrap(); FieldDescriptor::new(f, proto) }) .collect(); for (i, f) in fields.iter().enumerate() { assert!(index_by_number .insert(f.proto().get_number() as u32, i) .is_none()); assert!(index_by_name .insert(f.proto().get_name().to_owned(), i) .is_none()); assert!(index_by_name_or_json_name .insert(f.proto().get_name().to_owned(), i) .is_none()); let json_name = f.json_name().to_owned(); if json_name != f.proto().get_name() { assert!(index_by_name_or_json_name.insert(json_name, i).is_none()); } } MessageDescriptor { full_name, proto: proto.message, factory, fields, index_by_name, index_by_name_or_json_name, index_by_number, } } // Non-generic part of `new` is a separate function // to reduce code bloat from multiple instantiations. fn new_non_generic_by_pb_name( protobuf_name_to_package: &'static str, fields: Vec, file_descriptor_proto: &'static FileDescriptorProto, factory: &'static dyn MessageFactory, ) -> MessageDescriptor { let (path_to_package, proto) = match find_message_or_enum(file_descriptor_proto, protobuf_name_to_package) { (path_to_package, MessageOrEnum::Message(m)) => (path_to_package, m), (_, MessageOrEnum::Enum(_)) => panic!("not a message"), }; let mut field_proto_by_name = HashMap::new(); for field_proto in proto.get_field() { field_proto_by_name.insert(field_proto.get_name(), field_proto); } let mut index_by_name = HashMap::new(); let mut index_by_name_or_json_name = HashMap::new(); let mut index_by_number = HashMap::new(); let full_name = MessageDescriptor::compute_full_name( file_descriptor_proto.get_package(), &path_to_package, &proto, ); let fields: Vec<_> = fields .into_iter() .map(|f| { let proto = *field_proto_by_name.get(&f.name).unwrap(); FieldDescriptor::new(f, proto) }) .collect(); for (i, f) in fields.iter().enumerate() { assert!(index_by_number .insert(f.proto().get_number() as u32, i) .is_none()); assert!(index_by_name .insert(f.proto().get_name().to_owned(), i) .is_none()); assert!(index_by_name_or_json_name .insert(f.proto().get_name().to_owned(), i) .is_none()); let json_name = f.json_name().to_owned(); if json_name != f.proto().get_name() { assert!(index_by_name_or_json_name.insert(json_name, i).is_none()); } } MessageDescriptor { full_name, proto, factory, fields, index_by_name, index_by_name_or_json_name, index_by_number, } } /// Construct a new message descriptor. /// /// This operation is called from generated code and rarely /// need to be called directly. #[doc(hidden)] #[deprecated( since = "2.12", note = "Please regenerate .rs files from .proto files to use newer APIs" )] pub fn new( rust_name: &'static str, fields: Vec, file: &'static FileDescriptorProto, ) -> MessageDescriptor { let factory = &MessageFactoryImpl(marker::PhantomData::); MessageDescriptor::new_non_generic_by_rust_name(rust_name, fields, file, factory) } /// Construct a new message descriptor. /// /// This operation is called from generated code and rarely /// need to be called directly. #[doc(hidden)] pub fn new_pb_name( protobuf_name_to_package: &'static str, fields: Vec, file_descriptor_proto: &'static FileDescriptorProto, ) -> MessageDescriptor { let factory = &MessageFactoryImpl(marker::PhantomData::); MessageDescriptor::new_non_generic_by_pb_name( protobuf_name_to_package, fields, file_descriptor_proto, factory, ) } /// New empty message pub fn new_instance(&self) -> Box { self.factory.new_instance() } /// Message name as given in `.proto` file pub fn name(&self) -> &'static str { self.proto.get_name() } /// Fully qualified protobuf message name pub fn full_name(&self) -> &str { &self.full_name[..] } /// Message field descriptors. pub fn fields(&self) -> &[FieldDescriptor] { &self.fields } /// Find message field by protobuf field name /// /// Note: protobuf field name might be different for Rust field name. pub fn get_field_by_name<'a>(&'a self, name: &str) -> Option<&'a FieldDescriptor> { let &index = self.index_by_name.get(name)?; Some(&self.fields[index]) } /// Find message field by field name or field JSON name pub fn get_field_by_name_or_json_name<'a>(&'a self, name: &str) -> Option<&'a FieldDescriptor> { let &index = self.index_by_name_or_json_name.get(name)?; Some(&self.fields[index]) } /// Find message field by field name pub fn get_field_by_number(&self, number: u32) -> Option<&FieldDescriptor> { let &index = self.index_by_number.get(&number)?; Some(&self.fields[index]) } /// Find field by name // TODO: deprecate pub fn field_by_name<'a>(&'a self, name: &str) -> &'a FieldDescriptor { // TODO: clone is weird let &index = self.index_by_name.get(&name.to_string()).unwrap(); &self.fields[index] } /// Find field by number // TODO: deprecate pub fn field_by_number<'a>(&'a self, number: u32) -> &'a FieldDescriptor { let &index = self.index_by_number.get(&number).unwrap(); &self.fields[index] } }