blob: 53ea2ce2cf9ab28485bd23f3f9e67422bd3d02a7 [file] [log] [blame]
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;
use std::collections::HashMap;
use std::marker;
trait MessageFactory: Send + Sync + 'static {
fn new_instance(&self) -> Box<dyn Message>;
}
struct MessageFactoryImpl<M>(marker::PhantomData<M>);
impl<M> MessageFactory for MessageFactoryImpl<M>
where
M: 'static + Message + Default + Clone + PartialEq,
{
fn new_instance(&self) -> Box<dyn Message> {
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<FieldDescriptor>,
index_by_name: HashMap<String, usize>,
index_by_name_or_json_name: HashMap<String, usize>,
index_by_number: HashMap<u32, usize>,
}
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<M: Message>() -> &'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<FieldAccessor>,
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<FieldAccessor>,
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<M: 'static + Message + Default + Clone + PartialEq>(
rust_name: &'static str,
fields: Vec<FieldAccessor>,
file: &'static FileDescriptorProto,
) -> MessageDescriptor {
let factory = &MessageFactoryImpl(marker::PhantomData::<M>);
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<M: 'static + Message + Default + Clone + PartialEq>(
protobuf_name_to_package: &'static str,
fields: Vec<FieldAccessor>,
file_descriptor_proto: &'static FileDescriptorProto,
) -> MessageDescriptor {
let factory = &MessageFactoryImpl(marker::PhantomData::<M>);
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<dyn Message> {
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]
}
}