use std::ops::Deref; use protobuf::reflect::EnumDescriptor; use protobuf::reflect::EnumValueDescriptor; use protobuf::reflect::FieldDescriptor; use protobuf::reflect::FileDescriptor; use protobuf::reflect::MessageDescriptor; use protobuf::reflect::OneofDescriptor; use protobuf_parse::ProtobufAbsPath; use protobuf_parse::ProtobufAbsPathRef; use protobuf_parse::ProtobufIdentRef; use protobuf_parse::ProtobufRelPath; use protobuf_parse::ProtobufRelPathRef; use crate::customize::Customize; use crate::gen::field::rust_field_name_for_protobuf_field_name; use crate::gen::file_and_mod::FileAndMod; use crate::gen::map::map_entry; use crate::gen::message::message_name_to_nested_mod_name; use crate::gen::paths::proto_path_to_rust_mod; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust::rel_path::RustRelativePath; use crate::gen::strx::capitalize; pub(crate) struct RootScope<'a> { pub file_descriptors: &'a [FileDescriptor], } impl<'a> RootScope<'a> { fn packages(&'a self) -> Vec<FileScope<'a>> { self.file_descriptors .iter() .map(|fd| FileScope { file_descriptor: fd, }) .collect() } // find enum by fully qualified name pub fn _find_enum(&'a self, fqn: &ProtobufAbsPath) -> EnumWithScope<'a> { match self.find_message_or_enum(fqn) { MessageOrEnumWithScope::Enum(e) => e, _ => panic!("not an enum: {}", fqn), } } // find message by fully qualified name pub fn find_message(&'a self, fqn: &ProtobufAbsPath) -> MessageWithScope<'a> { match self.find_message_or_enum(fqn) { MessageOrEnumWithScope::Message(m) => m, _ => panic!("not a message: {}", fqn), } } // find message or enum by fully qualified name pub fn find_message_or_enum(&'a self, fqn: &ProtobufAbsPath) -> MessageOrEnumWithScope<'a> { assert!(!fqn.is_root()); self.packages() .into_iter() .flat_map(|p| p.find_message_or_enum_abs(fqn)) .next() .expect(&format!("enum not found by name: {}", fqn)) } } #[derive(Clone, Debug)] pub(crate) struct FileScope<'a> { pub file_descriptor: &'a FileDescriptor, } impl<'a> Deref for FileScope<'a> { type Target = FileDescriptor; fn deref(&self) -> &Self::Target { self.file_descriptor } } impl<'a> FileScope<'a> { fn package(&self) -> ProtobufAbsPath { ProtobufAbsPath::package_from_file_descriptor(self.file_descriptor) } pub fn to_scope(&self) -> Scope<'a> { Scope { file_scope: self.clone(), path: Vec::new(), } } fn find_message_or_enum( &self, name: &ProtobufRelPathRef, ) -> Option<MessageOrEnumWithScope<'a>> { self.find_messages_and_enums() .into_iter() .filter(|e| e.protobuf_name_to_package().as_ref() == name) .next() } fn find_message_or_enum_abs( &self, name: &ProtobufAbsPathRef, ) -> Option<MessageOrEnumWithScope<'a>> { let name = name.to_owned(); match name.remove_prefix(&self.package()) { Some(rem) => self.find_message_or_enum(&rem), None => None, } } // find all enums in given file descriptor pub fn find_enums(&self) -> Vec<EnumWithScope<'a>> { let mut r = Vec::new(); self.to_scope().walk_scopes(|scope| { r.extend(scope.enums()); }); r } /// Find all messages in given file descriptor pub fn find_messages(&self) -> Vec<MessageWithScope<'a>> { let mut r = Vec::new(); self.to_scope().walk_scopes(|scope| { r.extend(scope.messages()); }); r } /// Find all messages in given file descriptor, except map messages pub fn find_messages_except_map(&self) -> Vec<MessageWithScope<'a>> { self.find_messages() .into_iter() .filter(|m| !m.is_map()) .collect() } /// find all messages and enums in given file descriptor pub fn find_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> { let mut r = Vec::new(); self.to_scope().walk_scopes(|scope| { r.extend(scope.messages_and_enums()); }); r } } #[derive(Clone, Debug)] pub(crate) struct Scope<'a> { pub file_scope: FileScope<'a>, pub path: Vec<MessageDescriptor>, } impl<'a> Scope<'a> { pub(crate) fn file_descriptor(&self) -> FileDescriptor { self.file_scope.file_descriptor.clone() } // get message descriptors in this scope fn message_descriptors(&self) -> Vec<MessageDescriptor> { if self.path.is_empty() { self.file_scope.file_descriptor.messages().collect() } else { self.path.last().unwrap().nested_messages().collect() } } // get enum descriptors in this scope fn enum_descriptors(&self) -> Vec<EnumDescriptor> { if self.path.is_empty() { self.file_scope.file_descriptor.enums().collect() } else { self.path.last().unwrap().nested_enums().collect() } } // get messages with attached scopes in this scope pub fn messages(&self) -> Vec<MessageWithScope<'a>> { self.message_descriptors() .into_iter() .map(|message| MessageWithScope { scope: self.clone(), message, }) .collect() } // get enums with attached scopes in this scope pub fn enums(&self) -> Vec<EnumWithScope<'a>> { self.enum_descriptors() .into_iter() .map(|en| EnumWithScope { scope: self.clone(), en, }) .collect() } // get messages and enums with attached scopes in this scope pub fn messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> { self.messages() .into_iter() .map(|m| MessageOrEnumWithScope::Message(m)) .chain( self.enums() .into_iter() .map(|m| MessageOrEnumWithScope::Enum(m)), ) .collect() } // nested scopes, i. e. scopes of nested messages fn nested_scopes(&self) -> Vec<Scope<'a>> { self.message_descriptors() .into_iter() .map(|m| { let mut nested = self.clone(); nested.path.push(m); nested }) .collect() } fn walk_scopes_impl<F: FnMut(&Scope<'a>)>(&self, callback: &mut F) { (*callback)(self); for nested in self.nested_scopes() { nested.walk_scopes_impl(callback); } } // apply callback for this scope and all nested scopes fn walk_scopes<F>(&self, mut callback: F) where F: FnMut(&Scope<'a>), { self.walk_scopes_impl(&mut callback); } pub fn rust_path_to_file(&self) -> RustRelativePath { RustRelativePath::from_idents( self.path .iter() .map(|m| message_name_to_nested_mod_name(m.name())), ) } pub fn path_str(&self) -> String { let v: Vec<&str> = self.path.iter().map(|m| m.name()).collect(); v.join(".") } pub fn prefix(&self) -> String { let path_str = self.path_str(); if path_str.is_empty() { path_str } else { format!("{}.", path_str) } } pub fn protobuf_path_to_file(&self) -> ProtobufRelPath { ProtobufRelPath::from_components(self.path.iter().map(|m| ProtobufIdentRef::new(m.name()))) } pub fn protobuf_absolute_path(&self) -> ProtobufAbsPath { let mut r = self.file_scope.package(); r.push_relative(&self.protobuf_path_to_file()); r } pub fn file_and_mod(&self, customize: Customize) -> FileAndMod { FileAndMod { file: self.file_scope.file_descriptor.proto().name().to_owned(), relative_mod: self.rust_path_to_file(), customize, } } } pub(crate) trait WithScope<'a> { fn scope(&self) -> &Scope<'a>; fn file_descriptor(&self) -> FileDescriptor { self.scope().file_descriptor() } // message or enum name fn name(&self) -> &ProtobufIdentRef; fn name_to_package(&self) -> String { let mut r = self.scope().prefix(); r.push_str(&self.name()); r } fn protobuf_name_to_package(&self) -> ProtobufRelPath { let r = self.scope().protobuf_path_to_file(); r.append_ident(ProtobufIdentRef::new(self.name())) } /// Return absolute name starting with dot fn name_absolute(&self) -> ProtobufAbsPath { let mut path = self.scope().protobuf_absolute_path(); path.push_simple(self.name()); path } // rust type name of this descriptor fn rust_name(&self) -> RustIdent { let rust_name = capitalize(&self.name()); RustIdent::new(&rust_name) } fn rust_name_to_file(&self) -> RustIdentWithPath { self.scope() .rust_path_to_file() .into_path() .with_ident(self.rust_name()) } // fully-qualified name of this type fn rust_name_with_file(&self) -> RustIdentWithPath { let mut r = self.rust_name_to_file(); r.prepend_ident(proto_path_to_rust_mod( self.scope().file_descriptor().name(), )); r } } #[derive(Clone, Debug)] pub(crate) struct MessageWithScope<'a> { pub scope: Scope<'a>, pub message: MessageDescriptor, } impl<'a> WithScope<'a> for MessageWithScope<'a> { fn scope(&self) -> &Scope<'a> { &self.scope } fn name(&self) -> &ProtobufIdentRef { ProtobufIdentRef::new(self.message.name()) } } impl<'a> MessageWithScope<'a> { pub fn into_scope(mut self) -> Scope<'a> { self.scope.path.push(self.message); self.scope } pub fn to_scope(&self) -> Scope<'a> { self.clone().into_scope() } pub fn fields(&self) -> Vec<FieldWithContext<'a>> { self.message .fields() .into_iter() .map(|field| FieldWithContext { field, message: self.clone(), }) .collect() } pub fn oneofs(&self) -> Vec<OneofWithContext<'a>> { self.message .oneofs() .into_iter() .map(|oneof| OneofWithContext { message: self.clone(), oneof, }) .collect() } pub fn mod_name(&self) -> RustIdent { message_name_to_nested_mod_name(self.message.name()) } /// This message is a special message which is a map. pub fn is_map(&self) -> bool { map_entry(self).is_some() } } #[derive(Clone, Debug)] pub(crate) struct EnumWithScope<'a> { pub scope: Scope<'a>, pub en: EnumDescriptor, } impl<'a> EnumWithScope<'a> { pub fn values(&self) -> Vec<EnumValueWithContext<'a>> { self.en .values() .into_iter() .map(|v| EnumValueWithContext { en: self.clone(), proto: v, }) .collect() } // find enum value by protobuf name pub fn value_by_name(&self, name: &str) -> EnumValueWithContext<'a> { self.values() .into_iter() .find(|v| v.proto.proto().name() == name) .unwrap() } } #[derive(Clone, Debug)] pub(crate) struct EnumValueWithContext<'a> { pub en: EnumWithScope<'a>, pub proto: EnumValueDescriptor, } impl<'a> EnumValueWithContext<'a> { pub fn rust_name(&self) -> RustIdent { // TODO: camel case or something. RustIdent::new(self.proto.name()) } } impl<'a> WithScope<'a> for EnumWithScope<'a> { fn scope(&self) -> &Scope<'a> { &self.scope } fn name(&self) -> &ProtobufIdentRef { ProtobufIdentRef::new(self.en.name()) } } pub(crate) enum MessageOrEnumWithScope<'a> { Message(MessageWithScope<'a>), Enum(EnumWithScope<'a>), } impl<'a> WithScope<'a> for MessageOrEnumWithScope<'a> { fn scope(&self) -> &Scope<'a> { match self { MessageOrEnumWithScope::Message(m) => m.scope(), MessageOrEnumWithScope::Enum(e) => e.scope(), } } fn name(&self) -> &ProtobufIdentRef { match self { MessageOrEnumWithScope::Message(m) => m.name(), MessageOrEnumWithScope::Enum(e) => e.name(), } } } #[derive(Clone)] pub(crate) struct FieldWithContext<'a> { pub field: FieldDescriptor, pub message: MessageWithScope<'a>, } impl<'a> Deref for FieldWithContext<'a> { type Target = FieldDescriptor; fn deref(&self) -> &Self::Target { &self.field } } impl<'a> FieldWithContext<'a> { pub fn is_oneof(&self) -> bool { self.field.containing_oneof().is_some() } pub fn oneof(&self) -> Option<OneofWithContext<'a>> { match self.field.containing_oneof() { Some(oneof) => Some(OneofWithContext { message: self.message.clone(), oneof, }), None => None, } } } #[derive(Clone)] pub(crate) struct OneofVariantWithContext<'a> { pub oneof: &'a OneofWithContext<'a>, pub field: FieldDescriptor, } #[derive(Clone)] pub(crate) struct OneofWithContext<'a> { pub oneof: OneofDescriptor, pub message: MessageWithScope<'a>, } impl<'a> OneofWithContext<'a> { pub fn field_name(&'a self) -> RustIdent { return rust_field_name_for_protobuf_field_name(self.oneof.name()); } // rust type name of enum pub fn rust_name(&self) -> RustIdentWithPath { let type_name = RustIdent::from(capitalize(self.oneof.name())); self.message .to_scope() .rust_path_to_file() .into_path() .with_ident(type_name) } pub fn variants(&'a self) -> Vec<OneofVariantWithContext<'a>> { self.message .fields() .into_iter() .filter(|f| f.field.containing_oneof().as_ref() == Some(&self.oneof)) .map(|f| OneofVariantWithContext { oneof: self, field: f.field, }) .collect() } }